[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
",ans);
33 }
34 
35 int main()
36 {
37     work();
38     return 0;
39 }


 
原文地址:https://www.cnblogs.com/Yuzao/p/7289953.html