[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
把三角形旋转,变成
4
3 7
2 2 3
2 8 2 49
这样条件就变成了取(i,j)时,要取(i,1~j)和(i-1,1~j-1)
因此可以从右到左,一列一列的转移状态。
F[i][j][k]表示第i列,第j行的数被取,且共取k个数的最优方案。
F[i][j][k]=max(F[i+1][x][k-j])+sigma(a[1][i]…a[j][i]).(x>=j-1).
注意m的数据范围,较原题修改过。
时间为O(n^3*m) 这样已经做完了,但仍可以优化。
可以用s[j][i]前缀和优化掉sigma(a[1][i]…a[j][i])
用g[i][j][k]表示max(f[i][j][k]..f[i][n-i+1][k])
这样时间为O(n^2*m),空间O(n^2*m),效率极高,已无法继续优化。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int a[51][51],n,m,f[51][51][2501],ans; 7 int main() 8 {int i,j,k,l; 9 cin>>n>>m; 10 for (i=1;i<=n;i++) 11 { 12 for (j=n;j>=i;j--) 13 scanf("%d",&a[j][i]); 14 } 15 for (i=1;i<=n;i++) 16 { 17 for (j=1;j<=i;j++) 18 a[i][j]+=a[i][j-1]; 19 } 20 for (i=1;i<=n;i++) 21 { 22 for (j=0;j<=i;j++) 23 { 24 for (k=(j+1)*j/2;k<=i*(i-1)/2+j;k++) 25 { 26 for (l=j-1;l<i;l++) 27 f[i][j][k]=max(f[i][j][k],f[i-1][l][k-j]+a[i][j]); 28 } 29 } 30 } 31 for (i=1;i<=n;i++) 32 for (j=0;j<=i;j++) 33 ans=max(ans,f[i][j][m]); 34 cout<<ans; 35 return 0; 36 }