codevs1257 打砖块
在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,……最下面一层仅有一块砖。第i层的砖块从左至右编号为1,2,……i,第i层的第j块砖有一个价值a[i,j](a[i,j]<=50)。下面是一个有5层砖块的例子。如果你要敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它,若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖。
你的任务是从一个有n(n<=50)层的砖块堆中,敲掉(m<=500)块砖,使得被敲掉的这些砖块的价值总和最大。
你将从文件中读入数据,数据的第一行为两个正整数,分别表示n,m,接下来的第i每行有n-i+1个数据,分别表示a[i,1],a[i,2]……a[i,n – i + 1]。
输出文件中仅有一个正整数,表示被敲掉砖块的最大价值总和。
4 5
2 2 3 4
8 2 7
2 3
49
19
敲掉第一层的四块砖,再敲掉第二层的第一块砖,2+2+3+4+8=19
正解:DP
解题报告:
今天一班选拔赛三试,T2就是这道题,我在考场上想了+调试了2个小时才切掉了,还拍了下才确保AC的。
f[i][j][k]表示第i列取前j行总共取了k个的最优值。为什么设计这样的状态呢?显然按行DP的话要考虑约束条件很不好做,按列做的话就会好处理一些,并且从右往左做可以避免重复计算(待会儿说)。
首先我们可以明确如果我们选了一个点相当于是选了一个三角形,在按列摆出的图中靠左对齐的情况下其实就是选取了一个等腰直角三角形。那么我们就可以DP了,我们对于当前列的每一行都考虑从右边的哪些行转移过来。因为不能算重复,所以答案贡献只能加上当前列的自下往上前缀和再加上右边的可选的最优值(不会重叠)。所以我们做到第i列第j行时就只能选取i-1列的j-1行到i-1行作为可转移对象(如果小于j-1的话就会导致被当前三角形覆盖)。另外如果当前这一列一个都不选的话呢?那就不妨从0开始枚举j(行的坐标),这样就可以了。注意下边界条件。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int inf = (1<<20); 21 const int MAXN = 150; 22 const int MAXM = 520; 23 int n,m,ans; 24 int a[MAXN][MAXN],sum[MAXN][MAXN]; 25 int f[MAXN][MAXN][MAXM]; 26 27 inline int getint() 28 { 29 int w=0,q=0; 30 char c=getchar(); 31 while((c<'0' || c>'9') && c!='-') c=getchar(); 32 if (c=='-') q=1, c=getchar(); 33 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 34 return q ? -w : w; 35 } 36 37 inline void work(){ 38 n=getint(); m=getint(); 39 for(int i=1;i<=n;i++) 40 for(int j=n;j>=i;j--) 41 a[j][i]=getint(); 42 for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) sum[i][j]=sum[i][j-1]+a[i][j]; 43 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=0;k<=m;k++) f[i][j][k]=-inf; 44 //f[1][1][1]=a[1][1]; ans=a[1][1]; //for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) f[i][j][0]=0; 45 f[0][0][0]=0; 46 47 for(int i=1;i<=n;i++) 48 { 49 for(int j=0;j<=i;j++) 50 { 51 for(int k=j;k<=m;k++) 52 { 53 for(int l=max(j-1,0);l<i;l++) 54 { 55 f[i][j][k]=max(f[i-1][l][k-j]+sum[i][j],f[i][j][k]); 56 } 57 ans=max(ans,f[i][j][k]); 58 } 59 } 60 } 61 printf("%d",ans); 62 } 63 64 int main() 65 { 66 work(); 67 return 0; 68 }