HDU - 6007 Mr. Panda and Crystal 伪最短路 + 完全背包

1.题意:这里有n种晶石,一个魔法师有m的魔力,有些晶石他可以用魔力创造,有些晶石他只能用其他晶石合成,有些晶石它既能用魔力创造,也可以用晶石合成。每种晶石都有价格,问你这个魔法师用m的魔力最多能挣多少钱。

2.分析:

(1)因为最开始的时候,我们只能去合成能用魔力创造的晶石,再用这些晶石去合成其他。所以不论哪一种晶石我们都能用魔力消耗来表示他的花费!比如:

<1>晶石1只能由  晶石2 + 晶石3合成 :晶石1花费 = 魔力花费(晶石2) + 魔力花费(晶石3)

<2>晶石1 只能由魔力创造 : 晶石1花费 = 魔力花费(晶石1)

<3>晶石1既能由魔力创造也能由合成 : 晶石1花费 = min(魔力花费(晶石1) , 魔力花费(晶石2) + 魔力花费(晶石3));

(2)由上可知,我们只需要表示出每种晶石的最小魔力花费,就相当于该种物品的重量,已知每种物品的价值,背包容量为m,求所装的最大价值,这不就是完全背包吗!接下来完全背包就可以了!

(3)所以,问题的关键转变为求每种晶石的最小花费。因为每种晶石可能既能合成也能创造,也可能合成的原料也需要合成,所以这里就存在着连锁反应。不过有一点是可以明确地:我合成的花费最小,意味着原料的花费也要最小。因此,这里可以采用一个类似最短路的处理方式,每次所有晶石里花费最小的就是确定的,在用这个晶石去更新其他晶石。

3.代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
#define INF 0x3f3f3f3f
const int maxn = 210;
int w[maxn],v[maxn],dp[10010],goal[maxn];//最小魔力,价值,总价值,合成方案k的对象goal[k]
bool judge[maxn];
int n,m,k;
struct Node{//保存每个合成方案的  原料编号 + 数量
   int id,num;
   Node(int a,int b):id(a),num(b) {}
};
vector<int> vec[maxn];//vec[i]保存了以物品i为原料之一的所有合成方案
vector<Node> idea[maxn];//合成方案i的所有原料
int Sum(int k){//求方案k的总花费 = goal[k]的合成花费
    int res = 0;
    for(int i = 0;i<idea[k].size();i++){
        if(w[idea[k][i].id]==INF)return INF;//防止溢出RE!!有原料没有造出来,直接退出即可
         res+=(idea[k][i].num*w[idea[k][i].id]);
    }
    return res;
}
void Dijkstra(){
    priority_queue<P,vector<P>,greater<P> >que;
    memset(judge,0,sizeof(judge));
    for(int i = 1;i<=n;i++){
        if(w[i]<=m)que.push(P(w[i],i));//最开始先把只能由魔力创造的保存
    }
    while(!que.empty()){
        P p = que.top();
        que.pop();
        if(judge[p.second])continue;
        judge[p.second] = 1;//确定一个最小花费
        for(int i = 0;i<vec[p.second].size();i++){//更新所属的所有方案
            int d = vec[p.second][i];
            int ans = Sum(d);
            if(w[goal[d]] > ans){//方案对象变小了存入
                w[goal[d]] = ans;
                que.push(P(ans,goal[d]));
            }
        }
    }
}
void init(int a,int b){
    memset(dp,0,sizeof(dp));
    memset(w,INF,sizeof(w));
    for(int i = 0;i<=a;i++)vec[i].clear();
    for(int i = 0;i<=b;i++)idea[i].clear();
}
int main()
{
    int T;
    scanf("%d",&T);
    int t = 0;
    while(T--){
        t++;
        scanf("%d%d%d",&m,&n,&k);
        init(n,k);
        for(int i = 1;i<=n;i++){
            int op;
            scanf("%d",&op);
            if(op==0){
                scanf("%d",&v[i]);//不能创造,w[i] = INF
            }
            else{
                scanf("%d%d",&w[i],&v[i]);
            }
        }
        for(int i = 1;i<=k;i++){
             int u,len;
             scanf("%d%d",&u,&len);
             goal[i] = u;//合成方案i的对象是u
             for(int j = 0;j<len;j++){
                  int op,l;
                  scanf("%d%d",&op,&l);需要l个原料op
                  idea[i].push_back(Node(op,l));//方案i保存原料
                  vec[op].push_back(i);//原料保存所属方案
             }
        }
        Dijkstra();//求最小
        for(int i = 1;i<=n;i++){//完全背包
            for(int j = w[i];j<=m;j++){
                dp[j] = max(dp[j],dp[j - w[i]] + v[i]);
            }
        }
        printf("Case #%d: %d\n",t,dp[m]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82930384