题意:给你M,N,K,代表你有M点法力值,N个物品,K个制造方式
接下来N行,如果以1开头则代表既能卖又能合成,0代表只能卖。
然后K行,每行第一个数是要合成的东西,第二个数代表有几对,每对第一个数是那种物品,第二个是需要几个。
注意不能直接合成的可以通过制造方式来合成。然后问你花费完所有的法力制造的东西,能获得最大的金钱是多少
题解:首先处理出来对于所有物品所需要消耗的最小法力值,然后就是做一下完全背包就可以了。
消耗最小法力值可以通过spfa获得,一个技巧就是对于同属一个目标产物的原材料,可以用一个fa[]数组来指向目标产物的vector的数组下标.目标产物有两个vector,其中一个代表了是哪几种原材料,然后另一个对应的是这几种原材料的个数。
然后再利用head[]数组,得知一个目标产物的原材料的边的编号tot一定是连续的,那么就把这连续一段的边的编号指为当前目标产物的两个vector的数组下标就可以了。
具体详见代码。---强迫症是从0开始计的,所以说建边的时候要减一
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> using namespace std; const int INF=0x3f3f3f3f; int dp[10086],sell[208],pct[208],head[208]; bool vis[208]; int M,N,K,op,n,T,tas=1,tot; struct node { int v,next; } e[100861]; void add(int u,int v) { e[tot].v=v; e[tot].next=head[u]; head[u]=tot++; } vector<int>U[208]; vector<int>num[208]; int fa[100861]; void spfa() { queue<int>Q; for(int i=0; i<N; ++i) if(pct[i]!=INF) { Q.push(i),vis[i]=1; } while(!Q.empty()) { int u=Q.front(); Q.pop(); vis[u]=0; for(int i=head[u]; ~i; i=e[i].next) { bool ok=1; int ans=0; for(int j=0; j<(int)U[fa[i]].size(); ++j) { if(pct[U[fa[i]][j]]==INF) { ok=0; break; } ans+=pct[U[fa[i]][j]]*num[fa[i]][j]; } if(!ok) continue; if(ans<pct[e[i].v]) { pct[e[i].v]=ans; if(!vis[e[i].v]) Q.push(e[i].v),vis[e[i].v]=1; } } } } int main() { for(scanf("%d",&T); T--;) { memset(head,-1,sizeof(head)); memset(pct,INF,sizeof(pct)); tot=0; scanf("%d%d%d",&M,&N,&K); for(int i=0; i<N; ++i) { scanf("%d",&op); if(op==0) scanf("%d",&sell[i]); else scanf("%d%d",&pct[i],&sell[i]); } int now=0; for(int i=0; i<K; ++i) { int tar,par,ne,nn; scanf("%d%d",&tar,&par); if(!par) continue; U[now].clear(); num[now].clear(); int last=tot; --tar; for(int j=0; j<par; ++j) { scanf("%d%d",&ne,&nn); --ne; add(ne,tar); U[now].push_back(ne); num[now].push_back(nn); } for(int j=last; j<tot; ++j) fa[j]=now; ++now; } spfa(); memset(dp,0,sizeof(dp)); for(int i=0; i<N; ++i) for(int j=pct[i]; j<=M; ++j) dp[j]=max(dp[j],dp[j-pct[i]]+sell[i]); printf("Case #%d: %d ",tas++,dp[M]); } }