在一个凹槽中放置了 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;
输出文件仅一行为一个正整数,表示被敲掉砖块的最大价值总和。
4 5 2 2 3 4 8 2 7 2 3 49
19
Luogu:https://www.luogu.org/problem/show?pid=1437
动态规划
这是一道非常难想到的动态规划问题。 首先我们把矩阵左对齐,但发现如果我们直接在这个上面对行进行转移,我们发现是不满足转移的有序性的,因为第i行第j列是否可以敲掉取决于上面一个倒三角是否被敲掉,比如说这个图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15如果我们要敲掉7,则1,2都要敲掉。如果我们要敲掉12,则1,2,3,7,8都要敲掉。这给转移带来了麻烦。 如何解决呢?我们发现如果第i行第j列被敲掉了,那么要求对于\(\forall k \in [1,i-1]\),[k][j]一定被打掉了。 于是我们就想到话说这怎么想到的?把矩阵翻折过来。上面的矩阵就变成了这个样子
1 2 6 3 7 10 4 8 11 13 5 9 12 14 15简单点来说,就是把矩阵沿主对角线翻折,再向下对齐 这个翻转用程序表示就是:
for (int j=1;j<=n;j++) for(int i=j;i<=n;i++) Mat_new[i][j]=read();//read就是按照原矩阵的顺序从上至下从左至右读入那么我们就可以知道,如果要选择[i][j],那么[i][1~(j-1)]是一定要选的,并且我们还发现,原来的选一个数需要选择的上三角变成了更好处理的下三角。举个例子,比如说12 在原来的图中
1 2 [3] [4] [5] 6 7 [8] [9] 10 11 [12] 13 14 15把图翻折后
1 2 6 [3] 7 10 [4] [8] 11 13 [5] [9] [12] 14 15所以,我们设F[i][j][k]表示在新图中的第i行取前j个总共取了k个的最大的,那么我们只要枚举上一行是由那个转移过来的。 可以注意到,因为我们当前是在第j列,那么我们在上一行的枚举就至少得从j-1列开始。比如上面这个12的例子,那么就至少得从数字8(第2列)开始枚举,枚举到数字13(第4列) 所以我们就可以的得到动态转移方程(Arr就是我们翻转后的矩阵)\[F[i][j][k]=max(F[i][j][k],F[i-1][p][k-j]+\sum_{l=1}^{l<=j}Arr[i][j])\{p \in [1,j-1]\}\] 我们发现这个算法还有改进的余地,就是利用前缀和来优化\(\sum_{l=1}^{l<=j}Arr[i][j]\)的求解。 令\(Sum[i][j]=\sum_{k=1}^{k<=i}Arr[i][k]\),那么有\[Sum[i][j]=Sum[i][j-1]+Arr[i][j]\] 所以最终的转移方程就是\[F[i][j][k]=max(F[i][j][k],F[i-1][p][k-j]+Sum[i][j])\]
转载于:https://www.cnblogs.com/SYCstudio/p/7440920.html
相关资源:JAVA上百实例源码以及开源项目