城市間緊急救援(25 分)
作為一個城市的應急救援隊伍的負責人,你有一張特殊的全國地圖。在地圖上顯示有多個分散的城市和一些連接城市的快速道路。每個城市的救援隊數量和每一條連接兩個城市的快速道路長度都標在地圖上。當其他城市有緊急求助電話給你的時候,你的任務是帶領你的救援隊盡快趕往事發地,同時,一路上召集盡可能多的救援隊。
輸入格式:
輸入第一行給出4個正整數N、M、S、D,其中N(2≤N≤500)是城市的個數,順便假設城市的編號為0 ~ (N−1);M是快速道路的條數;S是出發地的城市編號;D是目的地的城市編號。
第二行給出N個正整數,其中第i個數是第i個城市的救援隊的數目,數字間以空格分隔。隨后的M行中,每行給出一條快速道路的信息,分別是:城市1、城市2、快速道路的長度,中間用空格分開,數字均為整數且不超過500。輸入保證救援可行且最優解唯一。
輸出格式:
第一行輸出最短路徑的條數和能夠召集的最多的救援隊數量。第二行輸出從S到D的路徑中經過的城市編號。數字間以空格分隔,輸出結尾不能有多余空格。
輸入樣例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
輸出樣例:
2 60
0 1 3
代碼及分析如下:
1 /* 2 3 目標: 4 求最短路徑長度和條數以及最短路徑下的最多救援隊數。(優先選擇距離最短的路徑,距離相同考慮救援隊數量) 5 代碼解釋: 6 Way數組:Way【i】從出發點到i點的最短路徑的數量 7 Sum數組:Sum【i】從出發點到i點的一條或多條最短路徑下的救援隊數量最多的路徑的救援隊數量 8 Peo數組:Peo【i】代表下表為i的點的初始救援隊個數 9 Move數組:Move【i】若為true代表小標為i的點已經被訪問過了,為false則沒有; 10 Dist數組:Dist【i】代表從出發點到i點的最短距離 11 思路: 12 基本的最短路問題(這里選擇迪傑斯特拉,注意它不能解決負權邊問題) 13 除此之外為了解決最短路徑條數和最短路下的最大救援隊數量引入Way數組和Sum數組 14 遞推公式 15 Way【i】=ΣWay【j】(j為使得Dist【i】最小的i點的前驅,可能有多個),顯然邊界條件Way【s】=1(s為出發點); 16 Sum【i】= Sum【j】+Peo【i】(j為最短路上i的前驅) 17 */ 18 #include <iostream> 19 #include <stdio.h> 20 #include <stdlib.h> 21 using namespace std; 22 #define Max 500 23 #define Sky 99999 //表示兩點之間沒有路徑時的距離 24 int distances[Max][Max],Top,Start,End,Edge; //分別為 兩城市間距離的二維數組 城市個數 起點 終點 邊數 25 int Dist[Max],Move[Max],Peo[Max],Sum[Max],Way[Max]; 26 void Dis() 27 { 28 int i,j; 29 //初始化邊界條件 30 for(i=0;i<Top;i++) 31 { 32 Dist[i]=distances[Start][i]; //把最短距離初始化為起點到該點的直接距離 33 Move[i]=false; //每個點初始化為都沒有訪問 34 } 35 Dist[Start]=0; //起點到本身距離為零 36 Move[Start]=true; //起點已經被訪問 37 Sum[Start]=Peo[Start]; //團隊數僅為起點團隊數 38 Way[Start]=1; //路徑條數為1 39 40 for(i=1;i<=Top-1;i++) //除了起點還要循環top-1 次才能完成所有定點的訪問 41 { 42 int minw=Sky; //保存距離出發點最近的點距離出發點的距離,初始化為最大 43 int k; //保存該點的編號 44 for(j=0;j<Top;j++)//遍歷每一個頂點 45 { 46 if(!Move[j]) //j沒有被訪問的話 47 { 48 if(minw>Dist[j])//如果找到距離出發點更近的點,更新minw和k 49 { 50 minw=Dist[j]; 51 k=j; 52 } 53 } 54 } 55 56 Move[k]=true; //更新過的k頂點處理完畢 標記數組為true 57 58 for(j=0;j<Top;j++)//遍歷每一個頂點 59 { 60 if(!Move[j])//如果沒有訪問(處理完畢) 61 { 62 if(Dist[j]==Dist[k]+distances[k][j])/*如果從起點到下標j點的距離等於從起點先到k再到j的距離*/ 63 { 64 Way[j]+=Way[k];//加和 65 if(Sum[j]<Sum[k]+Peo[j])//路徑距離相同時,團隊數更多 66 { 67 Sum[j]=Sum[k]+Peo[j];//團隊數更新 68 } 69 } 70 if(Dist[j]>Dist[k]+distances[k][j])/* 如果從起點到下標j點的距離等於從起點先到k再到j的距離*/ 71 { 72 Dist[j]=Dist[k]+distances[k][j]; 73 Sum[j]=Sum[k]+Peo[j]; 74 Way[j]=Way[k]; 75 } 76 77 } 78 } 79 } 80 } 81 void Dfs(int end,int Sump) 82 { 83 if(end==Start)//邊界條件 84 { 85 return; 86 } 87 for(int j=0;j<Top;j++)//遍歷每一個頂點 88 { 89 if(Sump-Peo[end]==Sum[j] && Dist[end]-distances[end][j]==Dist[j])/*尋找當前點的前驅點*/ 90 { 91 Dfs(j,Sum[j]); 92 cout<<j<<""; 93 } 94 } 95 96 } 97 int main( ) 98 { 99 cin>>Top>>Edge>>Start>>End; 100 int i,j; 101 for(i=0;i<Top;i++) 102 for(j=0;j<Top;j++) 103 distances[i][j]=Sky; 104 105 for(i=0;i<Top;i++) 106 { 107 scanf("%d",&Peo[i]); 108 } 109 110 for(i=0;i<Edge;i++) 111 { 112 int x,y,z; 113 scanf("%d%d%d",&x,&y,&z); 114 distances[x][y]=distances[y][x]=z;//存入直接相連城市間的距離z 115 } 116 Dis(); 117 cout<<Way[End]<<""<<Sum[End]<<endl; 118 119 Dfs(End,Sum[End]); 120 cout<<End<<endl; 121 122 123 return 0; 124 }
堅持不懈地努力才能成為大神!