DP
首先考虑如何将答案DP转移出来
记$dp[i][j]$表示使用了$i$次魔法,用了$j$个金币时的最大获利
因为可以将几个药水合成一个药水
那么在转移时会发现还需要处理出用了第$i$种药水由$j$次魔法合成的最小成本,记此为$tc[i][j]$
那么转移方程就是$dp[i][j]=min(dp[i-q][j-tc[p][q]]+w[p]-tc[p][q])$
$w$表示这个药水的售价
那么现在就要处理出$tc$数组
对于$tc[i][j]$,因为当前合成i需要一次操作,那么分配给其原料的合成次数为$j-1$
$tc[i][j]=max(sum_{k=1}^{Number}tc[a[k]][t[k]])$
且$sum_{k}^{Number}t[k]=j-1$
$a$数组表示这个合成这个药水的原料,$t$为给这个原料分配合成的次数,$Number$为原料总数
那么这个表达式也是可以DP转移的
记$tmp[i][j]$表示前$i$个原料,用了$j$次魔法的最小成本
填表转移即可
注意此处合成同一个药水的配方可能有多个
那么在这多个配方中$tc$选取最佳的即可
#include <bits/stdc++.h> #define inf (int)1e9 using namespace std; int n,m,v,k,tmp[100][100],tc[100][100]; int dp[45][1100]; struct node { int v,w; }sh[100]; struct magic { int p,h; vector <int> a; }d[300]; int main() { scanf("%d%d%d%d",&n,&m,&v,&k); for (int i=1;i<=n;i++) scanf("%d%d",&sh[i].v,&sh[i].w); for (int i=1;i<=m;i++) { scanf("%d%d",&d[i].p,&d[i].h); for (int j=1;j<=d[i].h;j++) { int num; scanf("%d",&num); d[i].a.push_back(num); } } for (int i=1;i<=n;i++) { for (int j=1;j<=k;j++) tc[i][j]=inf; tc[i][0]=sh[i].v; } for (int j=1;j<=k;j++) { for (int i=1;i<=m;i++) { for (int p=0;p<j;p++) tmp[0][p]=tc[d[i].a[0]][p];//初始值 for (int p=1;p<(int)d[i].a.size();p++) { for (int q=0;q<j;q++) { tmp[p][q]=inf; for (int r=0;r<=q;r++) tmp[p][q]=min(tmp[p][q],tmp[p-1][r]+tc[d[i].a[p]][q-r]);//tmp的DP转移 } } tc[d[i].p][j]=min(tc[d[i].p][j],tmp[(int)d[i].a.size()-1][j-1]);//在多个方案中取最优 } } for (int i=0;i<=k;i++) { for (int j=0;j<=v;j++) dp[i][j]=-inf; } dp[0][0]=0; for (int i=0;i<=k;i++) { for (int j=0;j<=v;j++) { for (int p=1;p<=n;p++) { for (int q=0;q<=i;q++) { if (tc[p][q]==inf) continue; if (j>=tc[p][q])//注意边界条件 dp[i][j]=max(dp[i][j],dp[i-q][j-tc[p][q]]+sh[p].w-tc[p][q]); //转移 } } } } int ans=0; for (int i=0;i<=k;i++) { for (int j=0;j<=v;j++) ans=max(ans,dp[i][j]);//统计答案 } printf("%d ",ans); }