题目背景
SOURCE:NOIP2015-SHY-10
题目描述
一块土地有 n 个连续的部分,用 H[1],H[2],…,H[n] 表示每个部分的最初高度。有 n 种泥土可用,他们都能覆盖连续的 k 个部分,第 i 种泥土的价格为 C[i],可以使 i,i+1,…,i+k-1 部分的高度增加 E[i](如果 i+k>n,那就覆盖 i,…,n ),我们必须满足以下条件:
1、每种泥土只能使用一次。
2、成本必须小于等于 m 。
要求在上述条件下,使得最低的部分的高度尽量高,请求出这个高度。
输入格式
第一行三个整数 n,m,k,表示土地有几个部分,最大预算成本以及每种泥土能覆盖的部分数。
接下来 n 行,每行三个整数 H[i],E[i],C[i]。
输出格式
输出一个整数,表示在满足条件的情况下,最低部分的高度的最大值。
样例数据 1
输入
4 20 1
1 3 5
1 7 3
4 6 9
3 5 13
输出
3
备注
【数据范围】
对 30% 的输入数据:1≤n≤20 。
对 100% 的输入数据:1≤k≤11;1≤n≤100;0≤m;H[i],E[i],C[i]≤106 。
分析
看这名字就知道是 dp 了,然后由于每个位置放不放泥土都有可能会对下一个位置产生影响,也就是在 k 长度以内的会有影响。我们就需要记录一下对于当前位置 v ,它前面 k-1 个位置放不放泥土的状态,状压dp搞定
“使得最低的部分的高度尽量高”这句话告诉我们赤裸裸的二分答案啊,那就ok啦
对于二分:我们每次得到一个 mid ,然后dp去找使得每一块土地的高度都大于等于 mid 所花的最小成本,如果小于 m 说明可以继续扩大 mid ,如果大于的话 mid 就应该减小
对于状压dp:我们定义 f [i] [status] 表示当前枚举到第 i 位,使得当前这位置的高度满足要求的最小成本(前 i - 1 位都已经满足条件高度大于等于 mid 且状态为status(表明哪些土地放了泥土))
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<algorithm>
#define ULL unsigned long long
#define ll long long
#define N 109
#define inf 2139062143
using namespace std;
int n,m,k,h[N],e[N],c[N];
int f[N][2050];
bool check(ll hi){
memset(f,127,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;++i){
for(int st=0;st<(1<<k);++st){
if(f[i-1][st]<inf){
int add=0;
for(int j=0;j<k-1;++j)
if((1<<j)&st) add+=e[i-j-1];//统计贡献
if(add+h[i]>=hi){
if((1<<k-1)&st) f[i][(st^(1<<k-1))<<1]=min(f[i][(st^(1<<k-1))<<1],f[i-1][st]);
//如果原第 k 位上有1,我们就把它变为0,然后右移
else f[i][st<<1]=min(f[i][st<<1],f[i-1][st]);
}
if(add+h[i]+e[i]>=hi){
if((1<<k-1)&st) f[i][((st^(1<<k-1))<<1)+1]=min(f[i][((st^(1<<k-1))<<1)+1],f[i-1][st]+c[i]);
else f[i][(st<<1)+1]=min(f[i][(st<<1)+1],f[i-1][st]+c[i]);
}
}
}
}
int ans=inf;
for(int i=0;i<(1<<k);++i)
ans=min(ans,f[n][i]);
return ans<=m;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
int i,j,minn=1e6;
for(i=1;i<=n;++i)
{
scanf("%d%d%d",&h[i],&e[i],&c[i]);
minn=min(minn,h[i]);
}
ll l=(ll)minn,r=101000000,ans;
while(l<=r){//可怜二分又写wa,今天重去搞到一个模板,这次是真正的了,是金牌大佬的写法,记住了!!
ll mid=l+r>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%lld",ans);
return 0;
}
二分模板再打一遍!
int l,r,ans;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d",ans);