【算法练习】最短路/枚举 百练poj1062:昂贵的聘礼

题目链接:http://bailian.openjudge.cn/practice/1062

参考链接:https://www.cnblogs.com/yinzm/p/5937219.html

1062:昂贵的聘礼

总时间限制: 

1000ms

内存限制: 

65536kB

描述

年轻的探险家来到了一个印第安部落里。在那里他和酋长的女儿相爱了,于是便向酋长去求亲。酋长要他用10000个金币作为聘礼才答应把女儿嫁给他。探险家拿不出这么多金币,便请求酋长降低要求。酋长说:"嗯,如果你能够替我弄到大祭司的皮袄,我可以只要8000金币。如果你能够弄来他的水晶球,那么只要5000金币就行了。"探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。另外他要告诉你的是,在这个部落里,等级观念十分森严。地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。他是一个外来人,所以可以不受这些限制。但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。因此你需要在考虑所有的情况以后给他提供一个最好的方案。
为了方便起见,我们把所有的物品从1开始进行编号,酋长的允诺也看作一个物品,并且编号总是1。每个物品都有对应的价格P,主人的地位等级L,以及一系列的替代品Ti和该替代品所对应的"优惠"Vi。如果两人地位等级差距超过了M,就不能"间接交易"。你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。

输入

输入第一行是两个整数M,N(1 <= N <= 100),依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了N个物品的描述。每个物品的描述开头是三个非负整数P、L、X(X < N),依次表示该物品的价格、主人的地位等级和替代品总数。接下来X行每行包括两个整数T和V,分别表示替代品的编号和"优惠价格"。

输出

输出最少需要的金币数。

样例输入

1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0

样例输出

5250

题目理解:

首先一开始我没有成功把这个图抽象出来,关于这个替代品的问题,我们可以从替代品到被替代物品建立一条有向边,边的权值是优惠价,然后再建立一个起点,起点到N个物品有N条有向边,边权是物品的原价。

重点是迪杰斯特拉的写法有点忘了哈!!

复习 !

1、建立图的思路

2、迪杰斯特拉增加的条件:

最高的等级与最低的等级之间的差不能超过M,这里是特别容易弄错的地方,我之前以为是如果A与B之间的等级差小于M,B与C之间的等级差小于M就能一直从A交换到C……

解题关键:最终的我们得到的最短路,里面肯定是有一个点X的等级是最低的,那么我们可以在原图上找到所有的比这个X点等级高的,而且那些点与X点之间的等级差必须小于等于M,最终的路径肯定是这些点里面产生的。所以我们枚举每一个可能的点,将这个被枚举的点当作X点,然后找到所有满足上述条件的其他点,在这些点之间跑单源最短路。每次枚举X点我都能得到一个最短路径值,最终我们取最小的路径值便是结果。

我依然用的邻接表存图

AC代码是:

//昂贵的聘礼
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <map>
#include <vector>
#include <iostream>
using namespace std;
int M,N,P,L,X;
#define INF 0x3f3f3f3f
struct Node{
    int price;
    int level;
    int subCount;
    int subNum[110];
    int subPrice[110];
}node[200];
//用邻接链表存图
struct E{
    int next;
    int c;
};
vector<E> G[110];
bool mark[110];
int Dis[110];   //距离向量 所有结点从0出发已知的最短路径

//枚举x点
//那么我们可以在原图上找到所有的比这个X点
// 等级高的,而且那些点与X点之间的等级差必须小于等于M
int dijkstra(int x){
    //初始化距离向量和mark
    for(int i=0;i<=N;i++){
        Dis[i]=INF;
        mark[i]= false;
    }

    //要找到0到1的最短路
    Dis[0]=0;
    mark[0]=true;
    int newP=0;
    //再找N次  因为自己增加了一个结点
    for(int i=1;i<=N;i++){
        for(int j=0;j<G[newP].size();j++){
            int t=G[newP][j].next;
            int c=G[newP][j].c;
            if(mark[t]==true) continue;
            //增加了这一个条件的迪杰斯特拉最短路
            if( !(node[x].level<=node[t].level && node[t].level-node[x].level<=M)) continue;
            if(Dis[t]==INF || Dis[t]>Dis[newP]+c){
                Dis[t]=Dis[newP]+c; //更新距离向量
            }
        }
        //这个求最小值的放错位置
        int min=INF;
        for(int j=1;j<=N;j++){
            if(mark[j]==true || Dis[j]==INF) continue;
            if(Dis[j]<min){
                min=Dis[j];
                newP=j;
            }
        }
        mark[newP]= true;
    }
    return Dis[1];
}
int main(){
    while(cin>>M>>N){
        for(int i=1;i<=N;i++){
            cin>>node[i].price>>node[i].level>>node[i].subCount;
            for(int j=1;j<=node[i].subCount;j++){  //也是从1开始编号
                cin>>node[i].subNum[j]>>node[i].subPrice[j];
            }
        }

        //建立图
        //结点0 到所有N个结点有一条边
        //替代品到被替代品有一条有向边
        for(int i=1;i<=N;i++){
            E tmp;
            tmp.c=node[i].price;
            tmp.next=i;
            G[0].push_back(tmp);
            for(int j=1;j<=node[i].subCount;j++){
                int id=node[i].subNum[j];
                int val=node[i].subPrice[j];
                E tp;
                tp.c=val;  //替代品替代的价格
                tp.next=i;
                G[id].push_back(tp);
            }
        }

        int ans=INF;
        for(int i=1;i<=N;i++){
            ans=min(dijkstra(i),ans);
        }
        cout<<ans<<endl;

    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40760678/article/details/100323353