题意:
热爱电子娱乐的同学们对于技能树一定不陌生.就是说,要先学习低级的垃圾技能,特定的几个垃圾技能学会了,才能学习更强的技能.比如说,要先学火球术和烈火墙,才能学习地狱烈焰.科技树也是一样.要先研究出电力和内燃机,才能研究工业学.那么,现在我们把问题简化,
这是一个技能树(或者科技树).格子上的数,是威力值.要先学会第一排第二个和第三个,才能学会第二排的第二个.每个技能学习的前提都是左上和右上的两个技能.假设现在有一个第一层有
N (N≤50)
个技能的技能树,而且技能点是有限的,只能学习
M (M≤500)
个技能,我们想知道最大的威力值之和是多少.
题解:
一开始看到依赖就想到最小割模型的
INF
边作为依赖的表现,但是这一题既有流量限制又要价值最优,所以不是最小割,然而费用流有处理不了依赖,所以也不能用费用流,然而 dp 状态又记不下,考场上我就这样挂了。
正解很奇妙,先把技能树落下来,变成:
14
33 15
2 33 4
22 13 76 3
31 23 11 2 23
我们把问题转化成去掉一些技能,这样去掉的技能一定是每行的一个前缀,而且后面去掉的一定不少于前面的,这样 dp 就好转移了,用
f[i][j][k]
表示前
i
行,第
#include<bits/stdc++.h>
typedef long long ll;
const int N = 52;
const int M = N * N;
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f *= -1;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
void cmax(ll &a, ll b) {if (b > a) a = b;}
int n, m, a[N][N], sum[N];
ll suf[N][N], f[N][N][M], ans;
int main() {
freopen("skill.in", "r", stdin);
freopen("skill.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int det = 0; det < n; det++)
for (int j = 1; j <= n - det; j++)
read(a[j + det][j]);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + i;
for (int i = 1; i <= n; i++)
for (int j = i; j > 0; j--)
suf[i][j] = suf[i][j + 1] + a[i][j];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= i; j++)
for (int k = std::min(sum[n] - m, sum[i] - i + j); k >= j; k--)
for (int l = j; l <= i + 1; l++)
cmax(f[i + 1][l][k + l], f[i][j][k] + suf[i + 1][l + 1]);
for (int j = 0; j <= n; j++)
for (int k = sum[n] - m; k <= sum[n]; k++)
cmax(ans, f[n][j][k]);
printf("%lld\n", ans);
return 0;
}