【算法練習】最短路/枚舉 百練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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章