敲 砖 块 敲砖块 敲砖块
在一个凹槽中放置了 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 块砖,求得分最多能有多少。
1 ≤ n ≤ 50 , 1 ≤ m ≤ n ∗ ( n + 1 ) / 2 1≤n≤50,1≤m≤n*(n+1)/2 1≤n≤50,1≤m≤n∗(n+1)/2
正 解 部 分 \color{red}{正解部分} 正解部分
若按行处理, 需要考虑这一行的每个元素是否可选, 但是 是否可选 这个问题不好处理 .
但若按列处理, 可以发现从上往下敲的砖块是连续的,
若从右往左处理, 又可以发现题目中的限制条件可以比较方便地处理 .
于是从右往左按列处理,
设 F [ i , j , k ] F[i, j, k] F[i,j,k] 表示 i i i 到 N N N 列, 第 i i i 列敲了 k k k 个砖块, 总共敲了 j j j 个砖块,
则 F [ i , j , k ] = max { F [ i + 1 , p , k − j ] + s u m [ i , j ] } ( p ≥ k − 1 ) F[i, j, k] = \max\{F[i+1, p, k-j] + sum[i, j]\}\ \ \ \ \ \ \ \ (p\ \geq \ k-1) F[i,j,k]=max{F[i+1,p,k−j]+sum[i,j]} (p ≥ k−1) .
A n s = max ( F [ 1 , i , M ] ) Ans = \max(F[1, i, M]) Ans=max(F[1,i,M])
时间复杂度 O ( N 4 ) O(N^4) O(N4) .
实 现 部 分 \color{red}{实现部分} 实现部分
- 状态转移的来源一定要限制 !!!
- 注意 M M M 的范围, F [ ] [ ] [ ] F[][][] F[][][] 的第二维要开到 N 2 N^2 N2 级别 !!!
20 p t s → 100 p t s 的 区 别 . . . 20pts \rightarrow 100pts 的区别... 20pts→100pts的区别...
#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;
}