题解 - 防御法阵

题目描述

牛牛悄悄溜进了敌人的阵地,准备破坏敌人的城墙和防御法阵,破坏防御法阵可以给牛牛带来经验值。
敌人的城墙分为N块,这些城墙排成一排,不成环。牛牛可以破坏M块城墙,他可以从任意一点开始不断破坏两侧相邻的城墙。
每块城墙下方刻有诸多防御法阵。每个法阵需要耗费不同的时间来破坏。同时,为了防止对手发现,牛牛在每块城墙下仅能停留T秒。经过的时间忽略不计。
破坏每个法阵会带来不同的经验值。并且,当相邻的城墙已经被破坏时,经验值会增加。
举例来讲,假设牛牛在某块城墙收获k点经验值,但该城墙一侧(按照破坏顺序的规则,显然只有一侧可能被破坏)收获经验值为k1,那么这块城墙的经验数变为k + k1 。而当某块城墙一侧有n块城墙被破坏,其实际收获的经验值为 k + k1 + k2+. . . +kn
一段城墙得到的经验值为这一段城墙中每一段得到的经验值之和。
显然,最终的经验值还和破坏顺序有关。
现在,牛牛想知道他能得到的经验值最大是多少。
Note:上述的k1, k2, . . . , kn是指城墙左侧(或右侧)连续n个城墙被破坏时获得的经验值。

输入

第一行三个整数 N, M, T,含义见题目描述。
接下来 N行,每行首先有正整数 K表示该块城墙下方的法阵数量,接下来 2K 个正整数,依次是第 1 个法阵破坏后的经验值 vi,第 1 法阵破坏的用时 ti。
第 2 个法阵破坏后的经验值和破坏用时,直到第 K 个法阵。

输出

一个正整数,表示牛牛能够得到的最大经验值。

样例

样例输入

【样例 1 输入】
5 3 5
2 1 5 9 2
3 1 10 1 2 2 3
1 9 4
3 1 10 5 2 1 4
4 8 8 9 9 7 7 6 1
【样例 2 输入】
5 5 5
2 1 5 9 2
3 1 10 1 2 2 3
1 9 4
3 1 10 5 2 1 4
4 8 8 9 9 7 7 6 1

样例输出

【样例 1 输出】
52
【样例 2 输出】
223

提示

【样例 1 说明】
城墙共 5 段,每段在 5 秒内破坏,不计相邻城墙的破坏效果加成的情况下,依次 能收到最多 9,3,9,5,6 点经验值。破坏 3 段,此时选择先破坏中间的城墙获得 9 点 经验值,再依次获得 5,6 点经验值,最终能够造成最大 52 点经验值。计算过程 如下 9+(9+5)+[(9+9+5)+6]=52。
【数据范围】
对于 20% 的数据,满足 K = 1
对于另外 20% 的数据,满足 M = 1
对于额外 20% 的数据,满足 M ≤ 3, K ≤ 5
对于 100% 的数据,满足0 < K ≤ 50,0 < T ≤ 200,0 < M < 30,0 < N < 10000, 0 < vi,ti ≤ 50
保证答案在 long long 范围内。

分析

1.对于每个城墙下面的法阵,可以用01背包求最大收益

2.由于只能破坏相邻的城墙,故可以用区间dp处理

g[i][j] 表示区间[l,r]的最大经验值
g [ l ] [ r ] = m a x ( g [ l ] [ r − 1 ] ∗ 2 + g [ r ] [ r ] , g [ l + 1 ] [ r ] ∗ 2 + g [ l ] [ l ] ) g[l][r] = max(g[l][r - 1] * 2 + g[r][r],g[l + 1][r] * 2 + g[l][l]) g[l][r]=max(g[l][r1]2+g[r][r],g[l+1][r]2+g[l][l])
时间复杂度 O(n * k^2 + nm)

代码

#pragma GCC optimize(2)
  
#include<bits/stdc++.h>
   
using namespace std;
   
typedef long long LL;
   
const int N = 10000 + 10,M = 200 + 10,K = 50 + 10;
 
int n,m,t,k;
int v[K],w[K];
int f[M];
map<int,LL> g[N];
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    cin >> n >> m >> t;
    for(int i = 1;i <= n;i++){
        cin >> k;
        for(int j = 1;j <= k;j++) cin >> w[j] >> v[j];
 
        memset(f,0,sizeof f);
 
        for(int j = 1;j <= k;j++)
            for(int p = t;p >= v[j];p--)
                f[p] = max(f[p],f[p - v[j]] + w[j]);
 
        g[i][i] = f[t];
    }
 
    for(int len = 2;len <= m;len++)
        for(int i = 1;i + len - 1 <= n;i++){
            int l = i,r = i + len - 1;
            g[l][r] = max(g[l][r - 1] * 2 + g[r][r],g[l + 1][r] * 2 + g[l][l]);
        }
 
    LL res = 0;
    for(int i = 1;i + m - 1 <= n;i++) res = max(res,g[i][i + m - 1]);
 
    cout << res;
 
    return 0;
}
  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值