cf1271D——经典 dp+贪心+图论

/*
该题目很好的将图论和dp的状态转移结合在了一起
首先有一个贪心策略:
    每个点都只能被离其最远(最靠右)的那个点派兵守卫,这个点可能是其自身
    正确性显然:士兵留到越后面,其产生的贡献越大,所以不到关键时刻,就不派兵守卫 
dp[i,j]表示进攻到i点时,剩余兵力为j时的最大收益
    转移时的策略:将i点连的所有点按收益从大到小排序,设该点派出l个兵,那么对应前状态就是dp[i-1][j-bi+l] 

复杂度:求最坏情况下的复杂度:初始k=5000
    那么每次转移时都要让第二重循环跑5000次,由于每个点最多只有一个入边,所以总复杂度为O(n^2) 

*/
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define N 5005
#define M 300005
int a[N],b[N],c[N],n,m,k;
int Max[N],dp[N][N];
vector<int>G[N];

int cmp(int a,int b){return c[a]>c[b];}

int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i],&b[i],&c[i]),Max[i]=i;
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        Max[v]=max(Max[v],u);
    }
    for(int i=1;i<=n;i++)
        G[Max[i]].push_back(i);
    for(int i=1;i<=n;i++)
        sort(G[i].begin(),G[i].end(),cmp);
    /*
    for(int i=1;i<=n;i++){
        for(auto x:G[i])cout<<x<<" ";
        puts("");
    }*/
    
    memset(dp,0x3f,sizeof dp);
    dp[0][k]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=5000;j++){
            int sum=0;
            if(j-b[i]>=a[i] && dp[i-1][j-b[i]]!=0x3f3f3f3f)//一个兵也不派 
                dp[i][j]=dp[i-1][j-b[i]];
            for(int l=0;l<G[i].size();l++){
                if(j-b[i]+l+1>5000)break;
                int v=G[i][l];
                sum+=c[v];
                if(j-b[i]+l+1>=a[i] && dp[i-1][j-b[i]+l+1]!=0x3f3f3f3f)
                    if(dp[i][j]!=0x3f3f3f3f)
                        dp[i][j]=max(dp[i][j],dp[i-1][j-b[i]+l+1]+sum);
                    else dp[i][j]=dp[i-1][j-b[i]+l+1]+sum;
            }
        }
    /*
    for(int i=1;i<=n;i++){
        for(int j=1;j<=20;j++)
            cout<<dp[i][j]<<" ";
        puts("");
    }*/
    
    int ans=-1;
    for(int j=0;j<=5000;j++)
        if(dp[n][j]!=0x3f3f3f3f)ans=max(ans,dp[n][j]);
    cout<<ans<<'\n';
            
    return 0;
}

posted on 2019-12-29 19:16  zsben  阅读(272)  评论(0编辑  收藏  举报

导航