[状压DP][二分]JZOJ 3521 道路覆盖

Description

ar把一段凹凸不平的路分成了高度不同的N段,并用H[i]表示第i段高度。现在Tar一共有n种泥土可用,它们都能覆盖给定的连续的k个部分。

对于第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]。

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。

分析

首先看数据范围,k那么小差不多就是状态压缩了

最小值最大,显然二分

我们二分最小高度,DP判断

(然鹅比赛的时候蠢到没有想到第i位由前k位影响?)

在display_lzy大爷机(you)缘(yi)巧(gao)合(zhi)之下,想到了上面括号里的内容

设f[i][s]为前i段和前k种泥土的状态为s时满足最小值也大于等于二分值的最小代价

如何维护高度?

设h[i][s]为从第i中往前数k种泥土的状态为s时的能增加的高度,然后如果当前位高度加上这个也小于二分值的话就不合法,f[i][s]=Inf

然后显然只能从s>>1和s>>1 + 1<<k-1转移过来

最后找f[n][s]中有没有小于等于m的就行了 

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
const int N=110;
int n,m,k;
int h[N],e[N],c[N],mh=2147483647,sh,sumh,mxbit;
int f[N][1<<12],h1[N][1<<12];

bool Solve(int height) {
    memset(f,0x7f,sizeof f);
    memset(h1,-0x7f,sizeof h1);
    f[0][0]=h1[0][0]=0;
    for (int i=1;i<=n;i++)
        for (int j=0;j<=mxbit;j++) {
            int from1=j>>1,from2=(j>>1)+(1<<k-1);
            f[i][j]=min(f[i-1][from1],f[i-1][from2])+j%2*c[i];
            h1[i][j]=max(h1[i-1][from1]+j%2*e[i],h1[i][j]);
            if (h1[i][j]+h[i]<height) f[i][j]=0x7f7f7f7f;
        }
    for (int i=0;i<=mxbit;i++)
        if (f[n][i]<=m) return 1;
    return 0;
}

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],&e[i],&c[i]),mh=min(mh,h[i]),sh=max(sh,h[i]),sumh+=c[i];
    sh+=sumh;mxbit=(1<<k)-1;
    int l=mh,r=sh,mid,ans=0;
    while (l<=r) {
        mid=l+r>>1;
        if (Solve(mid)) ans=max(ans,mid),l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/9826655.html