1. 程式人生 > >[二分][狀壓dp] Jzoj P3521 道路覆蓋

[二分][狀壓dp] Jzoj P3521 道路覆蓋

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。

 

題解

  • 最小值最大,顯然二分
  • 現在二分出來一個最低高度,思考一下怎麼判斷
  • 考慮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 }