思路分析

看到每一道菜有选和不选两种决策,我们想到了01背包。但是,和普通的01背包相比有个区别,就是做每一个决策是具有后效性的:选了i道菜会影响i+k道菜的价值,也就是说,菜的价值会和选的顺序有关。因此,我们需要利用一个贪心确定一个选择的顺序,令按照这一顺序选择答案最优。通过证明我们可以得到可以按照的要求顺序排序。
证明:设目前的时间为t,则,若顺序最优,则符合:
化简后就能得到我们上面的结论。
接下来就是普通的01背包了。这里还需要处理一个问题:因为结果可能是负数,我们需要将dp数组全部初始化为负无穷,然后将dp[0]设为0后再开始状态转移。这是需要学习的处理方式。

AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <climits>
using namespace std;
typedef long long ll;
const int maxn=60;
const int maxn2=1e6+10;
ll n,m,t;
struct ve{
    ll a,b,c;
};
vector<ve> de;
ll bi[maxn];
ll dp[maxn2];
ll lt[maxn2];
bool cmp(ve a1,ve a2){
    return a2.b*a1.c<a1.b*a2.c;
}
void pr(){
    for(int i=0;i<de.size();i++){
        ve pos=de[i];
        cout<<pos.a<<" "<<pos.b<<" "<<pos.c<<endl;
    }
}

int main()
{
    for(register int i=0;i<maxn2;i++) dp[i]=-INT_MAX;
    scanf("%lld %lld %lld",&n,&m,&t);
    for(int i=1;i<=n;i++){
        scanf("%lld",bi+i);
    }
    for(int i=1;i<=m;i++){
        ll j,aa,cc;
        scanf("%lld %lld %lld",&j,&aa,&cc);
        ve tmp;
        tmp.a=aa;
        tmp.c=cc;
        tmp.b=bi[j];
        de.push_back(tmp);
    }
    sort(de.begin(),de.end(),cmp);
    dp[0]=0;
//  pr();
    for(register int i=0;i<de.size();++i){
        ve pos=de[i];
        for(register int j=t;j>=pos.c;j--){
            dp[j]=max(dp[j],dp[j-pos.c]+pos.a-pos.b*j);
        }
    }
    ll ans=dp[1];
    for(int i=2;i<=t;i++){
        ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
    return 0;
}