https://www.luogu.com.cn/problem/P4037
由题目可知整个合成路径可以看作一个森林。
对于答案的统计,我们在处理完每棵树的消耗费用对应的最大力量以后,使用一个背包即可。
这样我们就可以统计出答案了。
现在问题在于如何处理出每一棵树的消耗费用对应的最大力量。
很明显这还是一个背包问题,但是这里需要合成,也就是说有一些物品买过来不能直接计算收益,再加上物品数量有限制,导致这个问题非常复杂。
那么我们可以考虑给背包多开一维状态,这边直接记录下有多少个此物品被用来合成,以此方便转移。
我们设置 \(dp_{i,j,k}\) 表示选择到第 \(i\) 件物品,选择 \(j\) 个此物品用来合成,总共消耗 \(k\) 个金币。
对于基本装备,我们已经知道了可以购买的数量,以及单价,可以直接多重背包。
但是由于我们多记了一维,我们在使用多重背包时还需要枚举合成数量,并且不计入这些用来合成的贡献。
对于高级装备的处理更加复杂,高级装备只能通过合成得到,那么我们就可以通过用来合成的物品数量以及价格推出高级装备的最多选择数量。
易得这个数量一定小于等于用来合成的物品数量。
之后我们枚举合成出来的数量,此时我们不需要考虑这些哪些用来继续合成,因此我们再设置一个 \(dp_i\) 表示在合成出目前枚举的量的情况下的金币消耗。
由于我们需要保证合成出来,所以我们要将背包内的值进行强制更新,也就是说每次枚举一个新的物品,要给背包内所有值都更新一次答案,这样才能保证有值加入,可以合成出来。
处理完成之后就可以直接更新三维状态的背包了。
这样就可以成功处理出每棵树的消耗费用对应的最大力量了。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,dp[55][105][2005],v[55],c[55],w[55],ans[2005],dp2[2005];
vector<pair<int,int>> e[55];
bool vis[55];
char op;
void dfs(int p){if(e[p].size()==0){//基本装备 w[p]=min(w[p],m/c[p]);for(int i=0;i<=w[p];i++){for(int j=0;j<=i;j++)dp[p][j][i*c[p]]=(i-j)*v[p];}return;}w[p]=1e18,c[p]=0;//高级装备 for(auto tmp:e[p]){int i=tmp.first,x=tmp.second;dfs(i);w[p]=min(w[p],w[i]/x);c[p]+=c[i]*x;}w[p]=min(w[p],m/c[p]);for(int i=0;i<=w[p];i++){memset(dp2,-0x3f,sizeof(dp2));dp2[0]=0;for(auto tmp:e[p]){int u=tmp.first,x=tmp.second;for(int j=m;j>=0;j--){int tmp=-1e18;for(int k=0;k<=j;k++)tmp=max(tmp,dp2[j-k]+dp[u][i*x][k]);dp2[j]=tmp;//强制更新 }}for(int j=0;j<=i;j++){for(int k=0;k<=m;k++)dp[p][j][k]=max(dp[p][j][k],dp2[k]+(i-j)*v[p]);}}
}
signed main(){memset(dp,-0x3f,sizeof(dp));cin>>n>>m;for(int i=1,k;i<=n;i++){cin>>v[i]>>op;if(op=='A'){cin>>k;for(int j=1,v,w;j<=k;j++){cin>>v>>w;e[i].push_back({v,w});vis[v]=1;}}else cin>>c[i]>>w[i];}for(int i=1;i<=n;i++){if(vis[i])continue;dfs(i);for(int j=m;j>=0;j--){//背包处理答案 for(int k=0;k<=j;k++){ans[j]=max(ans[j],ans[j-k]+dp[i][0][k]);}}}cout<<ans[m];return 0;
}