Luogu P1860 新魔法药水

DP

首先考虑如何将答案DP转移出来

记$dp[i][j]$表示使用了$i$次魔法,用了$j$个金币时的最大获利

因为可以将几个药水合成一个药水

那么在转移时会发现还需要处理出用了第i种药水由j次魔法合成的最小成本,记此为$tc[i][j]$

那么转移方程就是$dp[i][j]=max(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\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/huangchenyan/p/11305070.html
今日推荐