P1437 [HNOI2004]敲砖块 [dp]
在一个凹槽中放置了 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 块砖,求得分最多能有多少。
若按行处理, 需要考虑这一行的每个元素是否可选, 但是 是否可选 这个问题不好处理 .
但若按列处理, 可以发现从上往下敲的砖块是连续的,
若从右往左处理, 又可以发现题目中的限制条件可以比较方便地处理 .
于是从右往左按列处理,
设 表示 到 列, 第 列敲了 个砖块, 总共敲了 个砖块,
则 .
时间复杂度 .
- 状态转移的来源一定要限制 !!!
- 注意 的范围, 的第二维要开到 级别 !!!
#include<bits/stdc++.h>
#define reg register
const int maxn = 55;
int N;
int M;
int Ans;
int A[maxn][maxn];
int sum[maxn][maxn];
int F[maxn][maxn*(maxn+1)/2][maxn];
int main(){
scanf("%d%d", &N, &M);
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= N-i+1; j ++) scanf("%d", &A[i][j]);
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= N-i+1; j ++) sum[i][j] = sum[i][j-1] + A[j][i];
for(reg int i = N; i >= 1; i --) // cur_pos
for(reg int j = 1; j <= M; j ++) //tot
for(reg int k = 0; k <= j; k ++) //cur
for(reg int p = std::max(0, k-1); p <= std::min(N-i, j-k); p ++) //last
F[i][j][k] = std::max(F[i][j][k], F[i+1][j-k][p] + sum[i][k]);
for(reg int i = 0; i <= N; i ++) Ans = std::max(Ans, F[1][M][i]);
printf("%d\n", Ans);
return 0;
}