起初你有 \(k\) 个兵,你需要按顺序攻占 \(n\) 座城堡。为了占领第 \(i\) 座城堡,你需要至少 \(a_i\) 个士兵,士兵不会死,攻占成功后你可以获得 \(b_i\) 个士兵。攻占完一座城堡你可以派出至少一个兵驻守来获得这座城堡的分数 \(c_i\),你可以在你攻占完城堡 \(i\) 后立即派兵下车,或者在有向图上通向这个点的点处派兵下车。你需要在保证能攻占所有城堡的前提下,最大化你的得分。\(n \leq 5000, m\leq 3\times 10^5\),队伍中的人数无论如何不会超过 \(5000\)

Solution

对于城堡 \(i\),如果要对它派兵,那么一定会在最后一个能向他派兵的地方派兵

于是我们只需要考察其中的 \(n\) 条边即可

\(f[i][j]\) 表示在第 \(i\) 个城堡处,还剩 \(j\) 个士兵的最大收益

类似 \(01\) 背包的暴力转移

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5005;
const int K = 5001;
int n,m,k,r[N],a[N],b[N],c[N],f[N][N];
vector <int> g[N];

signed main() {
    ios::sync_with_stdio(false);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) cin>>a[i]>>b[i]>>c[i];
    for(int i=1;i<=n;i++) r[i]=i;
    for(int i=1;i<=m;i++) {
        int u,v;
        cin>>u>>v;
        r[v]=max(r[v],u);
    }
    for(int i=1;i<=n;i++) {
        g[r[i]].push_back(i);
    }
    memset(f,-0x3f,sizeof f);
    f[0][k]=0;
    for(int i=1;i<=n;i++) {
        for(int j=a[i]+b[i];j<=K;j++) f[i][j]=f[i-1][j-b[i]];
        for(int q:g[i]) {
            for(int j=0;j<=K;j++) //!
                f[i][j]=max(f[i][j],f[i][j+1]+c[q]);
        }
    }
    int ans = *max_element(f[n],f[n]+K);
    if(ans<0) cout<<-1;
    else cout<<ans;
}

相关文章: