[HNOI2004]敲砖块
题目描述
在一个凹槽中放置了 n 层砖块、最上面的一层有n 块砖,从上到下每层依次减少一块砖。每块砖
都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。
14 15 4 3 23
33 33 76 2
2 13 11
22 23
31
如果你想敲掉第 i 层的第j 块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第
i-1 层的第j 和第j+1 块砖。
你现在可以敲掉最多 m 块砖,求得分最多能有多少。
输入输出格式
输入格式:
输入文件的第一行为两个正整数 n 和m;接下来n 行,描述这n 层砖块上的分值a[i][j],满足
0≤a[i][j]≤100。
对于 100%的数据,满足1≤n≤50,1≤m≤n*(n+1)/2;
输出格式:
输出文件仅一行为一个正整数,表示被敲掉砖块的最大价值总和。
输入输出样例
输入样例#1:
4 5 2 2 3 4 8 2 7 2 3 49
输出样例#1:
19
题解:
讲讲我是怎么搞对的吧...
2 2 3 4
8 2 7
2 3
49
看样例这个图,会发现一行一行dp会存在后效性,那么不妨试着竖着dp
然后发现一个性质:如果(i,j)选了那么(1,j)到(i-1,j)就必须都选 后一列(1,j+1)到(i-1,j+1)也是
那么就发现类似于捆绑背包的方法去做了,一行必须捆起来把前i位都打掉.
然后就开始作死.......这tm实在是写着烦啊
然后看题解发现可以旋转过来
4
3 7
2 2 3
2 8 2 49
这样就变成了前i行和前j列的dp.
可以设状态为F[i][j][k]表示 前i行 第i行选了j个,一共选了k个的最大价值
好处理多了......
注意边界条件一堆........
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #define RG register 8 using namespace std; 9 const int N=55; 10 int a[N][N],f[N][N][1550],sum[N][N]; 11 void work() 12 { 13 int n,m; 14 scanf("%d%d",&n,&m); 15 for(int i=1;i<=n;i++) 16 for(int j=1,tmp=n-i+1;j<=tmp;j++) 17 scanf("%d",&a[n-j+1][i]); 18 for(int i=1;i<=n;i++) 19 for(int j=1;j<=i;j++) 20 sum[i][j]=sum[i][j-1]+a[i][j]; 21 int ans=0,p; 22 for(int i=1;i<=n;i++) 23 for(RG int g=0,lim=min(((i+1)*i)>>1,m);g<=lim;g++) 24 for(RG int j=0,tmp=min(i,g);j<=tmp;j++){ 25 for(RG int k=max(j-1,0),tmper=min(g-j,i-1);k<=tmper;k++){ 26 p=f[i-1][k][g-j]+sum[i][j]; 27 if(p>f[i][j][g])f[i][j][g]=p; 28 } 29 } 30 for(int j=0;j<=n;j++) 31 if(f[n][j][m]>ans)ans=f[n][j][m]; 32 printf("%d\n",ans); 33 } 34 35 int main() 36 { 37 work(); 38 return 0; 39 }