日常训练 20170620 技能树skill

题意:
  热爱电子娱乐的同学们对于技能树一定不陌生.就是说,要先学习低级的垃圾技能,特定的几个垃圾技能学会了,才能学习更强的技能.比如说,要先学火球术和烈火墙,才能学习地狱烈焰.科技树也是一样.要先研究出电力和内燃机,才能研究工业学.那么,现在我们把问题简化,
这里写图片描述
  这是一个技能树(或者科技树).格子上的数,是威力值.要先学会第一排第二个和第三个,才能学会第二排的第二个.每个技能学习的前提都是左上和右上的两个技能.假设现在有一个第一层有 N (N50) 个技能的技能树,而且技能点是有限的,只能学习 M (M500) 个技能,我们想知道最大的威力值之和是多少.
题解:
   一开始看到依赖就想到最小割模型的 INF 边作为依赖的表现,但是这一题既有流量限制又要价值最优,所以不是最小割,然而费用流有处理不了依赖,所以也不能用费用流,然而 dp 状态又记不下,考场上我就这样挂了。
   正解很奇妙,先把技能树落下来,变成:
14
33 15
2   33  4
22 13  76  3
31 23  11  2  23
我们把问题转化成去掉一些技能,这样去掉的技能一定是每行的一个前缀,而且后面去掉的一定不少于前面的,这样 dp 就好转移了,用 f[i][j][k] 表示前 i 行,第 i 行已经去掉了 j 个,一共去掉了 k 个,转移枚举一下去掉多少个,复杂度 O(n5)

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值