道路覆盖
(cover.cpp/c/pas)
【问题描述】
Tar把一段凹凸不平的路分成了高度不同的N段,并用H[i]表示第i段高度。现在Tar一共有n种泥土可用,它们都能覆盖给定的连续的k个部分。
对于第i种泥土,它的价格为C[i],可以使得区间[i,min(n,i+k-1)]的路段的高度增加E[i]。
Tar要设定一种泥土使用计划,使得使用若干泥土后,这条路最低的高度尽量高,并且这个计划必须满足以下两点要求:
(1)每种泥土只能使用一次。
(2)泥土使用成本必须小于等于M。
请求出这个最低的高度最高是多少。
【输入格式】
第一行为如上文所示的三个正整数:N,M,K。
接下来N行,每行3个如上文所示的正整数H[i],E[i],C[i]。
【输出格式】
输出有且只有一个数字,为最底部分的高度的最大值
【样例输入】
4 20 1
1 3 5
1 7 3
4 6 9
3 5 13
【样例输出】
3
【数据范围】
对于30%的数据:N≤20。
对于100%的数据:1≤K≤11,1≤N≤100,0≤M,H[i],E[i],C[i]≤1000000。
看到这个问题之后,想都没想就蛮力搜索,然后拿了40/100,看着大数据被卡了20ms.,痛心疾首啊!没加优化。。。
正解是对答案进行二分,每个统计用dp与二进制枚举
(跳石头,做过没?)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<limits.h>
using namespace std;
struct dirt{
int e,c;
}a[105];
int h[105];
int n,m,k,t1,t2,t3;
int ans;
void dfs(int tot,int i)
{
//搜索,没有任何优化,如果考虑优化的话也许就不是这个结果了
//printf("%d %d\n",tot,i);
if(i>n)
{
if(tot<=m)
{
int cmax=INT_MAX;
for(int j=1;j<=n;j++)
cmax=min(cmax,h[j]);
ans=max(ans,cmax);
}
return;
}
int ne=a[i].e;
int nc=a[i].c;
if(nc+tot<=m)
{
int lm=min(n,i+k-1);
for(int j=i;j<=lm;j++)
{
h[j]+=ne;
}
dfs(nc+tot,i+1);
for(int j=i;j<=lm;j++)
{
h[j]-=ne;
}
}
dfs(tot,i+1);
}
int dp[105][2055];
int Min(int xxx,int yyy)
{
if(xxx==-1)
return yyy;
if(yyy==-1)
return xxx;
return min(xxx,yyy);
}
//注意重写这个min,因为是最小的,但-1的时候时不可以到达的。
//所以说-1尽管比他们小,都得直接返回另外一个!!!
//否则你的程序会无限0!!!!
bool can(int limit)
{
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
for (int i=0;i<n;i++)
for (int j=0;j<(1<<k);j++)
//只要前K个,因为只有前K个的放置情况可以对其造成影响
//二进制枚举
if (dp[i][j]!=-1)
{
int sum=0;
for (int t=k-1;t>0;t--)
{
if ((1<<t)&j)
sum+=a[i-(k-1-t)].e;
//扫位 枚举
}
if (sum+h[i+1]>=limit)
dp[i+1][j>>1]=Min(dp[i+1][j>>1],dp[i][j]);
if (sum+h[i+1]+a[i+1].e>=limit)
dp[i+1][(j>>1)|(1<<(k-1))]=Min(dp[i+1][(j>>1)|(1<<(k-1))],dp[i][j]+a[i+1].c);
//这是DP的过程
}
for (int j=0;j<(1<<k);j++)
if (dp[n][j]!=-1)
if (dp[n][j]<=m) return 1;
return 0;
}
int l,r;
int main()
{
//freopen("cover.in","r",stdin);
//freopen("cover.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%d %d %d",&h[i],&a[i].e,&a[i].c);
}
if(n<=20)
{
//如果范围小的话就搜索一下,其实大数据也就20多毫秒的TLE,哭死
dfs(0,1);
cout<<ans<<endl;
return 0;
}
else
{
//否则二分!
l=0,r=100000000;
while(l+1<r)
{
int mid=(l+r)>>1;
if(can(mid))
l=mid;
else
r=mid-1;
}
//注意别出不来了到时候
}
if(can(l+1))
l++;//跳出的时候r==l+1
//我们当然希望要更大的,所以试下l+ 1
cout<<l<<endl;
return 0;
}