题源:https://codeforces.com/gym/101635/attachments
题意: n行,每行给定字符串s1,s2,s3代表一些菜谱名。s2和s3是煮成是的必要条件,然后给出c和v,分别代表s1的消耗和收获;
(注意:这个消耗并不可能是s1的真正消耗和收获,s1的最后消耗和收获是得加上s2和s3的)
然后问在不用超过C消耗的情况下最大收获是多少?
分析:这里我们可以想象到,一道有条件的菜要做成是要若干个“前提”菜做成的,这个过程就是拓扑排序!我们对于每个s1,s2,s3编号后就是拓扑排序了,s2,s3,就是相当于给予s1一个入度;
所有每次拓扑我们就枚举已经做成的s2和s3给最优的c和v给s1,然后最后对所有的菜进行01背包找出答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int inf=0x3f3f3f3f; const ll INF=1e18; const int N=1e6+6; const int M=1e5+5; struct node{ int u,id; }; vector<node>g[N]; ll dp[M],cc[M],vv[M],ind[M],book[N]; ll c[N],v[N]; int ID,cnt; char s1[50],s2[50],s3[50]; map<int,int>mp; map<string ,int>sign; queue<int>que; int getid(string x){ if(sign[x]==0){ sign[x]=++ID; } return sign[x]; } int main(){ int C,n; scanf("%d%d",&C,&n); for(int i=1;i<=n;i++){ scanf("%s%s%s%lld%lld",s1,s2,s3,&c[i],&v[i]); int z=getid(s1); int x=getid(s2); int y=getid(s3); ind[z]++; cc[z]=inf; mp[i]=z; g[x].pb({y,i}); g[y].pb({x,i}); } for(int i=1;i<=ID;i++){ if(ind[i]==0) que.push(i); } while(!que.empty()){ int x=que.front(); que.pop(); for(int i=0;i<g[x].size();i++){ node nowv=g[x][i]; int y=nowv.u,id=nowv.id,z=mp[id]; if(!book[id]&&ind[y]==0){ book[id]=1; ll cost=cc[x]+cc[y]+c[id]; ll val=vv[x]+vv[y]+v[id]; if(cc[z]>cost||(cc[z]==cost&&vv[z]<val)){ cc[z]=cost; vv[z]=val; } if(--ind[z]==0) que.push(z); } } } dp[0]=0; for(int i=1;i<=ID;i++){ for(int j=C;j>=0;j--) if(j-cc[i]>=0) dp[j]=max(dp[j],dp[j-cc[i]]+vv[i]); } ll maxx=0; ll lasc=0; for(int i=1;i<=C;i++){ if(dp[i]>maxx){ maxx=dp[i]; lasc=i; } } printf("%lld\n%lld\n",maxx,lasc); return 0; }