【2018/08/19】T2-状压dp+二分答案-dp

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/81842212

题目背景

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);

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/81842212