1. 程式人生 > >PAT甲級真題(迪傑斯特拉演算法)——1003. Emergency (25)

PAT甲級真題(迪傑斯特拉演算法)——1003. Emergency (25)

1003 Emergency (25 分)

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1​​ , c​2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2 .
Output Specification:


For each test case, print in one line two numbers: the number of different shortest paths between C​1 and C​2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.
Sample Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
Sample Output:
2 4

題目大意:

有m個城市,n條路,路的長度不一,在第i個城市上有一定數量的搜救隊。
現在從c1城出發,要抵達c2城,需要我們求的是c1到c2距離最短的路徑條數,和一條最短路徑上能召集到的救援隊的最大數量。

題目分析:

求單源最短路徑,可以用dijkstra演算法。

  • 城市中救援隊數量為點權,路長度為邊權;
  • 用count[i]表示出發點到i點的最短路徑數目,w[i]表示出發點到i點最短路徑上救援隊總數,dis[i]為出發點到i點的最短路徑長度;
  1. c1為出發點
  2. count[c1]的初值顯然為1,因為只有唯一一條路徑;
  3. w[c1]初值為c1城救援隊數量;
  4. dis[c1]為0。

在執行過程中,每次在未訪問到的城市中取距離出發點距離最短的城市u,當作跳板。

核心演算法:

    if(dis[u]+u到v的距離<dis[v]) {
    	更新dis[v],count[v],w[v]
    }
    else if(dis[u]+u到v的距離==dis[v]) {
    	更新count[v]
    	if(w[u]+v城市救援隊數量>w[v])
    		更新w[v]
    }
	

具體程式碼:

    #include<iostream>
    #include<cstdio>
    #include<climits>
    
    const int MAXN=510;
    const int inf=INT_MAX;
    using namespace std;
    
    int n,e[MAXN][MAXN],weight[MAXN],dis[MAXN],vis[MAXN]= {false};
    int count[MAXN];//count[i]表示出發點到i點的最短路徑數目
    int w[MAXN];//w[i]表示出發點到i點最短路徑上救援隊總數
    
    int main() {
    	int n,m,c1,c2;//m個城市,n條路 ,c1表示出發點,c2為終點
    	cin>>n>>m>>c1>>c2;
    	for(int i=0; i<n; i++) cin>>weight[i]; //輸入每個城市的隊伍數量,即點權
    	fill(dis,dis+MAXN,inf);
    	fill(e[0],e[0]+MAXN*MAXN,inf);//初始化
    	int a,b,len;
    	for(int i=0; i<m; i++) {
    		cin>>a>>b>>len;
    		e[a][b]=e[b][a]=len;
    	}
    
    	dis[c1]=0;
    	count[c1]=1;
    	w[c1]=weight[c1];//出發點到自身顯然距離為0,路徑數為1,最短路徑上救援隊總數為自身點權
    	for(int i=0; i<n; i++) {
    		int min,u;
    		min=inf;//min為一個比較標準
    		u=-1;//u為剩餘點集中距離出發點最短的點,即跳板
    		for(int j=0; j<n; j++) {
    			if(vis[j]==false&&dis[j]<min) {
    				min=dis[j];
    				u=j;
    			}
    		}
    		if(u==-1) break;
    		vis[u]=true;
    		for(int v=0; v<n; v++) {
    			if(vis[v]==false&&e[u][v]!=inf) {
    				if(dis[u]+e[u][v]<dis[v]) { //如果新路徑長度小於老路徑
    					dis[v]=dis[u]+e[u][v];
    					count[v]=count[u];//點v到出發點的最短路徑總數與u的相同
    					w[v]=w[u]+weight[v];//點i到出發點最短路徑上救援隊總數等於點i的點權加上w[u]
    				} else if(dis[u]+e[u][v]==dis[v]) { //如果新路徑長度等於老路徑
    					count[v]=count[v]+count[u];//v到出發點的最短路徑總數與u到出發點的最短路徑總數相加
    					if(w[u]+weight[v]>w[v])//若新路徑上的救援隊總數更多,則更新為新路徑的
    						w[v]=w[u]+weight[v];
    				}
    			}
    		}
    	}
    	cout<<count[c2]<<" "<<w[c2];
    	return 0;
    }