Dijkstra(迪傑斯特拉)算法刷題模板(附詳細註釋)及經典例題 - java語言

dijkstra的題目一般給出每條邊的起點、終點、權值,需要轉化成鄰接矩陣或鄰接表
鄰接矩陣的時間複雜度爲O(v^2),
鄰接表的時間複雜度爲O(v^2+E)
dijkstra算法用於求單源最短路徑,即某個頂點到其他所有頂點的最短路徑。
dijkstra算法不適用於存在負權值的邊的情況。
與《算法筆記》中另用數組d[]表示起點到各點的最短路徑不同,本模板直接在原來的鄰接矩陣中修改G[s][i],執行完dijkstra算法後,G[s][i]即爲起點到各點的最短路徑

dijkstra算法經典例題:

輸入頂點數n、邊數e、起始頂點s,下面e行爲每條邊的起點、終點、權值
輸出起點s到所有頂點的最短路徑、起點s到所有頂點的距離

樣例1:
輸入:
5 7 0
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60

輸出:
從0出發到0的最短路徑爲:0
從0出發到1的最短路徑爲:0 1
從0出發到2的最短路徑爲:0 3 2
從0出發到3的最短路徑爲:0 3
從0出發到4的最短路徑爲:0 3 2 4
0 10 50 30 60

樣例1的鄰接矩陣:
static int[][] w = {
{0,10,M,30,100},
{M,0,50,M,M},
{M,M,0,M,10},
{M,M,20,0,60},
{M,M,M,M,0}
};

樣例2:
輸入:
//6個頂點,8條邊,起點爲0號頂點。以下8行爲8條邊
//邊0->1的邊,權爲1,下同
6 8 0
0 1 1
0 3 4
0 4 4
1 3 2
2 5 1
3 2 2
3 4 3
4 5 3

輸出:
從0出發到0的最短路徑爲:0
從0出發到1的最短路徑爲:0 1
從0出發到2的最短路徑爲:0 1 3 2
從0出發到3的最短路徑爲:0 1 3
從0出發到4的最短路徑爲:0 4
從0出發到5的最短路徑爲:0 1 3 2 5
0 1 5 3 4 6

import java.util.*;
public class Dijkstra模板 {
	
	static int MAXV=999;					//最大頂點數,由具體題目決定
    static int M=999999;					//不能用Integer.MAX_VALUE,否則做加法後可能會溢出
	  
	static int n,m,s;  						//n爲頂點數,m爲邊數,s爲起始頂點編號
	static int[][] G=new int[MAXV][MAXV];  	//一個有向圖的鄰接矩陣,
//與《算法筆記》中另用數組d[]表示起點到各點的最短路徑不同,
//本模板直接在原來的鄰接矩陣中修改G[s][i],執行完dijkstra算法後,G[s][i]即爲起點到各點的最短路徑

	static int[] v=new int[MAXV];    		//標記數組,v[i]=1表示i點已訪問,初值均爲0
	
	static String[] path=new String[MAXV]; 	//存放從start到其他各點的最短路徑的字符串表示  
	
	//除了距離之外的第二標尺
//	static int cost[][]=new int[MAXV][MAXV]; cost代表新增的邊權
//	static int c[]=new int[MAXV];            c代表從起點到各點的花費
	
//	static int resource[]=new int[MAXV];     resource代表新增的點權
//	static int r[]=new int[MAXV];		     r代表從起點到各點獲得的資源
	
//	static int num[]=new int[MAXV];          num代表到各點的最短路徑條數。
	
    public static void main(String[] args) {  
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		m=sc.nextInt();
		s=sc.nextInt();

		for(int i=0;i<n;i++) {
			Arrays.fill(G[i], M);
			G[i][i]=0;
			path[i]=s+""; 
		} 
             
//      Arrays.fill(c, M);
//      c[s]=0;
//      r[s]=resource[s];
//      num[s]=1;

		for(int i=0;i<m;i++) {
			int v1=sc.nextInt();
			int v2=sc.nextInt();
			int weight=sc.nextInt();
//			int co=sc.nextInt();
			G[v1][v2]=weight;
//			G[v2][v1]=weight;  若爲無向圖,則加上這一句
//			cost[v1][v2]=co;
//			cost[v2][v1]=co;
		}
        
		dijkstra(s);

        for(int i=0;i<n;i++) {
            System.out.println("從"+s+"出發到"+i+"的最短路徑爲:"+path[i]);    
        }
		
		for(int i=0;i<n;i++) {
			System.out.print(G[s][i]+" ");
		}
		
		sc.close();
    }
    
    
	//dijkstra算法
    public static void dijkstra(int s){  
     //接受一個起點編號s(從0開始編號)  

        for(int i=0;i<n;i++)  //要加入n個頂點  
        {  
          //1、選出一個距離初始頂點s最近的未標記頂點  u、從s到u的距離min
            int u=-1;    
            int min=M;  
            for(int j=0;j<n;j++) {  
                if(v[j]==0&&G[s][j]<min) {  
                    min=G[s][j];  
                    u=j;  
                }
            }
            
            
          //2、選好u之後,看看u是否存在,若存在則更新狀態
          //若剩下的頂點都和起點s不連通,則u=-1,返回
            if(u==-1) return ;
          //將第i個選出的頂點u標記爲已求出最短路徑
            v[u]=1;  
            
            
          //3、以u爲中間點,修正從【起點s】到未訪問各點的距離  
			for(int j=0;j<n;j++) {
				if(v[j]==0) {
					if(G[s][u]+G[u][j]<=G[s][j]) {	//要求path時,必須寫成“<=”,否則可以只寫“<”
						G[s][j]=G[s][u]+G[u][j];
					
						//這裏進行其他某些操作
						path[j]=path[u]+" "+j;
//						c[j]=c[u]+cost[u][v];
//						r[j]=r[u]+resource[j];
//						num[j]=num[u];
				}
//若上個if條件中符號爲"<",且存在第二標尺,那麼還要另外考慮第一標尺相同時的情況
//					else if(G[s][u]+G[u][j]==G[s][j]) {
//					    if(當前的第二標尺優於之前){
//						path[j]=path[u]+" "+j;
//				}
//						c[j]=Math.min(c[j],c[u]+cost[u][v]);
//						r[j]=Math.max(r[j],r[u]+resource[j]);
//						num[j]+=num[u];
//					}
				}
			}
        }
    }
}

熟練掌握了dijkstra模板並且會做簡單題之後,我們來做一下下面這兩道難度較大的dijkstra算法的題目,其實可以可以看出來,難題一般都是在經典例題的基礎上改編的。
下面兩題都是PAT甲級考試的真題,原題即爲英文,套用上文中的dijkstra算法模板給出了java代碼的答案

下面這道題添加了兩個第二標尺:最短路徑數量num,每個城市的資源(救援隊)resource

PAT A1003 Emergency (25分)

原文鏈接:PAT A1003 Emergency
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, C​1and C​2 - 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 c​1 , 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 C​1​​ to C​2​​ .

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

下圖爲《算法筆記》379頁,對此題的解析
在這裏插入圖片描述

import java.util.*;
public class PAT_A1003_Emergency {
	
	static int M=100000000;
	static int MAXN=999;
	
	static int n;
	static int[][] G=new int[MAXN][MAXN];
	static int[] v=new int[MAXN];
	static int[] resource=new int[MAXN];
	static int[] r=new int[MAXN];
	static int[] num=new int[MAXN];
	
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		int m=sc.nextInt();
		int c1=sc.nextInt();
		int c2=sc.nextInt();

		for(int i=0;i<n;i++) {
			resource[i]=sc.nextInt();
		}
		
		for(int i=0;i<n;i++) {
			Arrays.fill(G[i],M);
		}
		G[c1][c1]=0;
		r[c1]=resource[c1];
		num[c1]=1;
		
		for(int i=0;i<m;i++) {
			int v1=sc.nextInt();
			int v2=sc.nextInt();
			int weight=sc.nextInt();
			G[v1][v2]=weight;
			G[v2][v1]=weight;
		}

		dijkstra(c1);

		System.out.print(num[c2]+" "+r[c2]);

	}
	
	public static void dijkstra(int c1) {

		for(int i=0;i<n;i++) {
			
			int u=-1;
			int min=M;
			for(int j=0;j<n;j++) {
				if(v[j]==0&&G[c1][j]<min) {
					min=G[c1][j];
					u=j;
				}
			}
			
			v[u]=1;
			
			for(int j=0;j<n;j++) {
				if(v[j]==0) {
					if(G[c1][u]+G[u][j]<G[c1][j]) {
						G[c1][j]=G[c1][u]+G[u][j];
						r[j]=r[u]+resource[j];
						num[j]=num[u];
						
					}else if(G[c1][u]+G[u][j]==G[c1][j]) {
						r[j]=Math.max(r[j], r[u]+resource[j]);
						num[j]+=num[u];
					}
				}
			}
		}
	}
}

下面這道題添加了兩個第二標尺:最短路徑數量num、每條邊的花費cost,並且要求求出從起點到終點的最短路徑及最短路徑長度

PAT A1030 Travel Plan (30分)

原文鏈接:PAT A1030 Travel Plan
A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

Input Specification:
Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:

City1 City2 Distance Cost

where the numbers are all integers no more than 500, and are separated by a space.

Output Specification:
For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.

Sample Input:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

Sample Output:
0 2 3 3 40

下圖爲《算法筆記》386頁,對此題的解析
在這裏插入圖片描述

import java.util.*;
public class PAT_A1030_TravelPlan {

	static int MAXV=510;
	static int M=99999;
	
	static int n;
	static int[][] G=new int[MAXV][MAXV];
	static int[] v=new int[MAXV];
	
	static int[][] cost=new int[MAXV][MAXV];
	static int[] c=new int[MAXV];
	static String[] path=new String[MAXV];
	
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		int m=sc.nextInt();
		int s=sc.nextInt();
		int d=sc.nextInt();
		
		for(int i=0;i<n;i++) {
			Arrays.fill(G[i], M);
			path[i]=s+"";
		}
		G[s][s]=0;
		Arrays.fill(c, M);
		c[s]=0;
		
		for(int i=0;i<m;i++) {
			int v1=sc.nextInt();
			int v2=sc.nextInt();
			int dis=sc.nextInt();
			int co=sc.nextInt();
			G[v1][v2]=G[v2][v1]=dis;
			cost[v1][v2]=cost[v2][v1]=co;
		}
		
		dijkstra(s);
		
		System.out.print(path[d]+" "+G[s][d]+" "+c[d]);

	}
	
	public static void dijkstra(int s) {
		for(int i=0;i<n;i++) {
			int u=-1;
			int min=M;
			for(int j=0;j<n;j++) {
				if(v[j]==0&&G[s][j]<min) {
					u=j;
					min=G[s][j];
				}
			}
			
			if(u==-1) return ;
			v[u]=1;
			
			for(int j=0;j<n;j++) {
				if(v[j]==0) {
					if(G[s][u]+G[u][j]<G[s][j]) {
						G[s][j]=G[s][u]+G[u][j];
						path[j]=path[u]+" "+j;
						c[j]=c[u]+cost[u][j];
					}else if(G[s][u]+G[u][j]==G[s][j]) {
						if(c[u]+cost[u][j]<c[j]) {
						path[j]=path[u]+" "+j;
						c[j]=c[u]+cost[u][j];
						
						}
					}
				}
			}		
		}
	}
}

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