大意
- 给定一个如下的三角形,每个格子有数字。
- 可以敲掉一些格子,若要敲掉当前格子,则必须敲掉左上和右上的格子。
- 若最多能敲掉 m 个格子,求最大的权值和。
- 第一行格子数目 n≤50 。
1 2 3 4 5 6
| 14 15 04 03 23 33 33 76 02 02 13 11 22 23 31
|
题解
可以发现,敲掉的应该是这样的:
可以想到 dp ,但不行,因为下一层的转移依赖上一层敲掉了哪些砖块,而显然不能状压。
考虑进行转换。
首先先向左推。
1 2 3 4 5
| 14 15 04 03 23 33 33 76 02 02 13 11 22 23 31
|
然后向左旋转 90° 。
1 2 3 4 5
| 23 03 02 04 76 11 15 33 13 23 14 33 02 22 31
|
这样,要敲掉的就变成了:
发现,每一行要敲的必须不多于上一行敲的加 1 。
可以 dp 了。
设 f(i,j,k) 为考虑完了第 i 行,该行敲了 j 个,一共敲了 k 个最大权值。
有转移:
f(i,j,k)=max(f(i−1,j′,k−l),j−1≤j′≤i−1)+sum(i,1…l)
代码
提交记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 60, MAXM = 1250;
int n, m; int a[MAXN][MAXN], b[MAXN][MAXN]; int sum[MAXN][MAXN];
void read() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= (n - i + 1); j++) cin >> a[i][j]; } }
void pre() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) b[n - j + 1][i] = a[i][j]; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) sum[i][j] = sum[i][j - 1] + b[i][j]; } }
int dp[MAXN][MAXN][MAXM]; int calc() { for (int i = 1; i <= n; i++) { for (int j = 0; j <= i; j++) { for (int k = j * (j + 1) / 2; k <= m; k++) { for (int l = max(j - 1, 0); l <= i - 1; l++) { dp[i][j][k] = max(dp[i][j][k], dp[i - 1][l][k - j] + sum[i][j]); } } } }
int res = 0; for (int i = 0; i <= n; i++) res = max(res, dp[n][i][m]); return res; }
int main() { read(); pre(); cout << calc() << endl; }
|