解題步驟:
1.初始化:設置mat[][]存放點之間的距離,vis[]存放點的選取情況,people[]存放初始時每個城市的人數,man[]存放到達每個城市的救援隊的最多的人數,num[]存放到達每個城市的最多的人數(在最短路徑的基礎之上),dist[]存放從起點開始到達每個城市的最短的路徑(隨着每次選取點而更新)
2.核心算法:按照Dijkstra算法思想,從起點出發,不斷選擇一個點使得通過該點到達其他點的距離比直接通過已知點到達的距離更短,則更新最短距離數組dist[],這里分為兩種情況1:確實是通過某個點能使得到達其他點的距離更短,則除了修改最短距離數組dist之外,此時通過該點為跳板達到點的行走方案數量就是到達該跳板點的行走方案數量,而到達該點的人數就是到達跳板點的人數加上該點本來就有的人數。(因為人數是必須以最短距離為基礎的,只要有更短的距離,則人數就要修改成那種方案下的人數)2:如果通過選中的點到達其他點的距離和原本已知點達到該點的距離是相等的,則需要增加到達該點的行走方案的總數,以及可能要修改同樣是到達該點的時最多可召集的救援隊的數量。方案增加的方法是原來到x點的走法數量加上到達跳板點的走法的數量,而人數的修改則是選取原來方案到x的人數和到達跳板點最多人數+x點的人數二者之間的最大值
3.注意點:對於初始化的時候,我們要注意的是除了設置所有從起點開始不可達的點的距離為無窮大(0x3f3f3f3f)之外, 我們巧妙的設置初始點到自己的距離為0,通過這樣的方式,第一個選中的跳板是出發點自身,則會為man[],num[]數組進行一遍賦值,這樣后續的核心算法的判斷和修改過程才能成立
1 #include<iostream> 2 #include<string.h> 3 using namespace std; 4 5 const int Max = 0x3f3f3f3f; 6 const int N = 505; 7 int mat[N][N]; 8 int dist[N]; //記錄隨着每次獲取一個新的點之后從起點開始到每個點的最短距離的最新情況 9 int people[N]; //記錄每個城市初始的救援隊的人數 10 int num[N]; //記錄從起點開始到達某個點的最短路徑的條數 11 int man[N]; //記錄從起點開始在最短路徑的基礎上到達某個城市的最多的人數 12 int vis[N]; //記錄某個城市是否被走過 1走過 0未走過 13 int n, m, s, t; 14 15 void init(){ 16 for(int i = 0; i < n; i++) scanf("%d", &people[i]); 17 memset(mat, Max, sizeof(mat)); 18 memset(vis, 0, sizeof(vis)); 19 memset(man, 0, sizeof(man)); 20 memset(num, 0, sizeof(num)); 21 for(int i = 1; i <= m; i++){ 22 int x, y, z; 23 scanf("%d%d%d", &x, &y, &z); 24 mat[x][y] = z; 25 mat[y][x] = z; 26 } 27 mat[s][s] = 0; //設置到達自己本身為0 則第一次必定會選擇自己 28 for(int i = 0; i < n; i++){ 29 dist[i] = mat[s][i]; 30 } 31 num[s] = 1; //設置初始到達起點的走法有1條 32 man[s] = people[s]; //設置到達起點的可以召集的人數為起點的人數 33 } 34 35 int search(){ 36 int k = -1; 37 int mmin = Max; 38 for(int i = 0; i < n; i++){ 39 if(vis[i] == 0 && dist[i] < mmin){ //這個點沒走過則選取一個最小的距離,確保每次選擇的點是到達該點的最短的路徑 40 k = i; 41 mmin = dist[i]; 42 } 43 } 44 //因為至少存在一條通路所以k的返回值不可能是-1 (若圖是不連通的則某個時刻k會返回-1) 45 return k; 46 } 47 48 void run(){ 49 for(int i = 1; i <= n; i++){ //有n個點 則需要查詢n-1次能走完所有的點 50 int k = search(); //查詢最短距離的點 51 vis[k] = 1; //標記走過該點 52 for(int j = 0; j <= n-1; j++){ 53 if(vis[j] == 0 && dist[j] > mat[k][j] + dist[k]){ 54 dist[j] = mat[k][j] + dist[k]; 55 num[j] = num[k]; //如果通過k到達j的路徑是最短的 則到達j的條數就是達到k的條數 56 man[j] = people[j] + man[k]; 57 } 58 //這里必須要用else if因為執行完上面的語句之后 dist[j] == mat[k][j] + dist[k]必定會成立則會導致再次執行下面的語句 59 else if(vis[j] == 0 && dist[j] == mat[k][j] + dist[k]){ 60 num[j] += num[k]; 61 man[j] = max(man[j], people[j] + man[k]); 62 } 63 } 64 } 65 printf("%d %d\n", num[t], man[t]); 66 } 67 68 int main(){ 69 while(scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){ 70 init(); 71 run(); 72 } 73 return 0; 74 }
