DP2 1008
hotel
Miss D和gnaw出去玩的时候,发现一个很奇怪的旅馆,宾馆老板特别喜欢数字4和7,如果一个房间里住4或7个人,他就会很开心,不然他甚至不想让这个房间里住人。现在告诉你每个房间住的人数(7人以内),将一个原在i号房间的人移动到j房间的代价是abs(i-j),要想能满足老板的要求,花费的代价是多少?
输入
第一行一个数n,表示房间数
第二行n个数,表示每个房间原先住着的人数
输出
一个数,表示按照老板要求最小的花费
如果不能按要求分配,输出-1
100%:1<=n<=1e5
题解
状态设计是最难想的,f[i][j]为第i个房间向后一个房间移动j个人且第i个房间满足条件的最小操作数。
因为一个房间最多7个人,所以最多向后移动7个人,不过也看是移进来,所以第二维14,以7为基底。
在转移的时候枚举从这个房间转出和下个房间转出多少人,这样可以考虑到所有情况。
最后从第n-1个房间找答案,因为第n个房间不能转出。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=100005; const int inf=1061109567; int n,a[maxn]; int f[maxn][20];//从第i个房间向i+1的房间出去j个人的最小操作(j<7为进入 template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } int abs(int x){return x<0 ? -x : x ;} bool cx(int x){ if(!x||x==4||x==7) return true; return false; } int main(){ freopen("hotel.in","r",stdin); freopen("hotel.out","w",stdout); read(n); for(int i=1;i<=n;i++) read(a[i]); memset(f,0x3f,sizeof(f)); f[0][7]=0; for(int i=0;i<n;i++) for(int j=0;j<=14;j++) if(f[i][j]!=inf){ int in=j-7; for(int k=0;k<=14;k++){ int out=k-7; if(cx(a[i+1]+in-out)) f[i+1][k]=min(f[i+1][k],f[i][j]+abs(out)); } } int ans=inf; for(int i=0;i<=14;i++) if(cx(a[n]+i-7))//n没有后继,所以从n-1找答案 ans=min(ans,f[n-1][i]); printf("%d", ans==inf ? -1 : ans); }
gift
Gnaw给Miss D准备了一个长为n的礼物,还让m个朋友来帮他完成这个礼物,每个人开始是站在位置p,最多可以装扮连续的长为x的部分,一个人可以不参与,一旦参与,他装扮的部分一定包含他一开始站的位置。每个人装扮长为1的部分能让整个礼物增加y的美丽度,gnaw想让整个礼物的美丽值最高,会是多少?
输入
第一行两个整数n m,表示礼物长度和朋友数。
接下来n行,每行3个整数,分别为一个人最多装扮长度,单位美丽值和初始位置。
输出
一个整数,表示整个礼物最大的美丽值
1 <= n <= 16 000
1 <= m <= 100
1 <= y <= 10 000
ps:只要p在他涂的区间就合法。
题解
可以想到f[i][j]表示前i个人涂前j个单位的最大美丽值,枚举区间右端点j和左端点k(k,j]。
f[i][j]=max(f[i-1][k]+(j-k)*y) k+1<=p<=j并且区间长度不超过j
这样就能保证区间一定选到了p
变一下形f[i][j]=max(f[i-1][k]-ky+jy)
jy是定值,所以只要f[i-1][k]-ky最大即可,可以用单调队列维护(滑动区间求最大值)
不过发现要对人按p排序,因为如果前面有人能够放而且对某个状态没有影响,那么那个状态就可以更大。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn=16005; const int maxm=105; int n,m; int f[maxm][maxn];//前i个人装饰前j个单位的最大值 int h,t,s[maxn]; struct person{ int x,y,pos; bool operator < (const person a){return pos<a.pos;} }a[maxm]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } int get(int i,int j,int w){return f[i][j]-j*w;} int main(){ freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); read(n);read(m); for(int i=1;i<=m;i++) read(a[i].x),read(a[i].y),read(a[i].pos); sort(a+1,a+m+1); for(int i=1;i<=m;i++){ h=1,t=0; for(int j=0;j<=n;j++){ f[i][j]=max(f[i-1][j],f[i][j-1]);//前缀求最大值 if(j<a[i].pos&&j+a[i].x>=a[i].pos){//把上一层的f放进去 while(h<=t&&get(i-1,s[t],a[i].y)<get(i-1,j,a[i].y)) t--; s[++t]=j; } if(j>=a[i].pos&&j<a[i].pos+a[i].x){//右端点在pos后才更新,保证选择了pos while(h<=t&&s[h]+a[i].x<j) h++; f[i][j]=max(f[i][j],get(i-1,s[h],a[i].y)+j*a[i].y); } } } printf("%d",f[m][n]); }