Description
ar把一段凹凸不平的路分成了高度不同的N段,并用H[i]表示第i段高度。现在Tar一共有n种泥土可用,它们都能覆盖给定的连续的k个部分。
对于第i种泥土,它的价格为C[i],可以使得区间[i,min(n,i+k-1)] 的路段的高度增加E[i]。
Tar要设定一种泥土使用计划,使得使用若干泥土后,这条路最低的高度尽量高,并且这个计划必须满足以下两点要求:
(1)每种泥土只能使用一次。
(2)泥土使用成本必须小于等于M。
请求出这个最低的高度最高是多少。
对于第i种泥土,它的价格为C[i],可以使得区间[i,min(n,i+k-1)] 的路段的高度增加E[i]。
Tar要设定一种泥土使用计划,使得使用若干泥土后,这条路最低的高度尽量高,并且这个计划必须满足以下两点要求:
(1)每种泥土只能使用一次。
(2)泥土使用成本必须小于等于M。
请求出这个最低的高度最高是多少。
Input
第一行为如上文所示的三个正整数:N,M,K。
接下来N行,每行3个如上文所示的正整数H[i],E[i],C[i]。
接下来N行,每行3个如上文所示的正整数H[i],E[i],C[i]。
Output
输出有且只有一个数字,为最底部分的高度的最大值
Sample Input
4 20 1
1 3 5
1 7 3
4 6 9
3 5 13
Sample Output
3
Data Constraint
对于30%的数据:N≤20。
对于100%的数据:1≤K≤11,1≤N≤100,0≤M,H[i],E[i],C[i]≤1000000。
对于100%的数据:1≤K≤11,1≤N≤100,0≤M,H[i],E[i],C[i]≤1000000。
题解
- 最小值最大,显然二分
- 现在二分出来一个最低高度,思考一下怎么判断
- 考虑dp,设f[i][s]为当前到第i位前k个位用泥土的状态为s(是否有用)
- 那么就很显然了,每次转移前现算出前k个给当前位的贡献,设为num
- 转移就有两种一种是当前位不选,一种是当前位选
- 状态转移方程就很显然了
- ①f[i+1][j>>1]=min(f[i+1][j>>1],f[i][j]);
②f[i+1][(j>>1)|(1<<(k-1))]=min(f[i+1][(j>>1)|(1<<(k-1))],f[i][j]+c[i+1]);
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 using namespace std; 5 int n,m,k,sum,mx,num,h[110],e[110],c[110],f[110][1<<12]; 6 bool check(int x) 7 { 8 memset(f,127,sizeof(f)),f[0][0]=0; 9 for (int i=0;i<=n-1;i++) 10 for (int j=0;j<=(1<<k)-1;j++) 11 { 12 num=0; 13 for (int z=1;z<=k-1;z++) if (j&(1<<z)) num+=e[i-k+z+1]; 14 if (num+h[i+1]>=x) f[i+1][j>>1]=min(f[i+1][j>>1],f[i][j]); 15 if (num+h[i+1]+e[i+1]>=x) f[i+1][(j>>1)|(1<<(k-1))]=min(f[i+1][(j>>1)|(1<<(k-1))],f[i][j]+c[i+1]); 16 } 17 for (int i=0;i<=(1<<k)-1;i++) if (f[n][i]<=m) return true; 18 return false; 19 } 20 int main() 21 { 22 freopen("cover.in","r",stdin),freopen("cover.out","w",stdout); 23 scanf("%d%d%d",&n,&m,&k); 24 for (int i=1;i<=n;i++) scanf("%d%d%d",&h[i],&e[i],&c[i]),sum+=e[i],mx=max(mx,h[i]); 25 int l=1,r=sum+mx; 26 while (l<r) 27 { 28 int mid=(l+r)>>1; 29 if (check(mid)) l=mid+1; else r=mid; 30 } 31 printf("%d",l-1); 32 }