求最短路徑

求最短路徑的兩個常見算法:
1,Floyd算法
代碼如下:dis[i][j]保存頂點i與j之間的距離,如果距離等於-1則表示兩點不可達;n表示圖中的結點數

for(int ik = 1;k <= n;k++){
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= n;j++){
			if(dis[i][k] == -1 || dia[k][j] == -1) continue;
			if(dis[i][j] == -1 || dis[i][k] + dis[k][j] < dis[i][j])
				dis[i][j] = dis[i][k] + dis[k][j];
		}
	}
}

2,Dijkstra算法求單源最短路徑
首先初始化,將源點加入集合k,從源點s出發到其它節點的距離是-1,
然後遍歷與集合k中的節點直接相連的邊,找出其中最短的邊,邊最小的節點被選爲下一個最短路徑確定的點,然後將該點加入集合k
代碼如下:map[][]表示存儲圖的鄰接矩陣,dis[i]存儲源點到節點的最短路徑;used[i] == 1表示節點i已經加入了集合k

void dijkstra(){
	//初始化
	for(int i = 1;i <= n;i++){
		dis[i] = map[i][j];
		used[i] = 0;
	}
	used[1] = 1;
	
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= n;j++){
			if(used[j] == 0 && dis[j] <= min){
				min = dis[j];
				k = j;
			}
		}
	}
	used[k] = 1;
	
	for(int j = 1;j <= n;j++){
		if(dis[k] + map[k][j] < dis[j])
			dis[j] = dis[k] + map[k][j];
	}
}
九度1447:求最短路 題目地址:http://ac.jobdu.com/problem.php?pid=1447 題目描述:

在每年的校賽裏,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?

輸入:

輸入包括多組數據。每組數據第一行是兩個整數N、M(N<=100,M<=10000),N表示成都的大街上有幾個路口,標號爲1的路口是商店所在地,標號爲N的路口是賽場所在地,M則表示在成都有幾條路。N=M=0表示輸入結束。接下來M行,每行包括3個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A與路口B之間有一條路,我們的工作人員需要C分鐘的時間走過這條路。輸入保證至少存在1條商店到賽場的路線。
當輸入爲兩個0時,輸入結束。

輸出:

對於每組輸入,輸出一行,表示工作人員從商店走到賽場的最短時間。

樣例輸入:
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
樣例輸出:
3
2
#include<iostream>
using namespace std;
 
#define N 101
int dis[N][N];
 
#define MAX 1000000
 
int main(){
    int n,m;
    while(cin >> n >> m){
        if(n == 0 &&m == 0) break;
         
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++){
                if(i == j) dis[i][j] = 0;
                else dis[i][j] = MAX;
            }
                 
         
        for(int i = 0;i < m;i++){
            int c1,c2,t;
            cin >> c1 >> c2 >> t;
            if(t < dis[c1][c2])
                dis[c1][c2] = dis[c2][c1] = t;
        }
         
        for(int k = 2;k < n;k++){
            for(int i = 1;i <= n;i++){
                for(int j = 1;j <= n;j++){
                    if(dis[i][k] == MAX || dis[k][j] == MAX) continue;
                    if(dis[i][j] == MAX || dis[i][k] + dis[k][j] < dis[i][j])
                        dis[i][j] = dis[i][k] + dis[k][j];
                }
            }
        }
        int ans = dis[1][n];
        cout << ans << endl;
    }
    return 0;
}
 
/**************************************************************
    Problem: 1447
    User: cherish
    Language: C++
    Result: Accepted
    Time:40 ms
    Memory:1560 kb
****************************************************************/
上面這是Floyd解法,下面再給出Dijkstra解法:

#include<iostream>
using namespace std;
 
#define N 101
int map[N][N];
int dis[N];
int used[N]; 
 
#define MAX 1000000
 
int main(){
    int n,m;
    while(cin >> n >> m){
        if(n == 0 &&m == 0) break;
         
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++){
                if(i == j) map[i][j] = 0;
                else map[i][j] = MAX;
            }
                 
         
        for(int i = 0;i < m;i++){
            int c1,c2,t;
            cin >> c1 >> c2 >> t;
            if(t < map[c1][c2])
                map[c1][c2] = map[c2][c1] = t;
        }
         
        //Dijkstra算法
        //初始化 dij_dis存儲從節點1到任意節點的距離 
        for(int i = 1;i <= n;i++) dis[i] = map[1][i]; 
        for(int i = 2;i <= n;i++) used[i] = 0;
        used[1] = 1;
         
        for(int i = 1;i <= n;i++){
            int min = MAX;
            int k;
            //遍歷所有沒有加入集合的節點,找到最短的邊 
            for(int j = 1;j <= n;j++){
                if(used[j] == 0 && dis[j] < min){
                    min = dis[j];
                    k = j;
                }
            }
            //把最短的邊對應的節點加入集合中 
            used[k] = 1;
            //根據新加入的最短路計算源點到其他節點的最短路徑 
            for(int j = 1;j <= n;j++){
                if(dis[k] + map[k][j] < dis[j])
                    dis[j] = dis[k] + map[k][j];
            }
             
        } 
         
          
        int ans = dis[n];
        cout << ans << endl;
    }
    return 0;
}
 
/**************************************************************
    Problem: 1447
    User: cherish
    Language: C++
    Result: Accepted
    Time:30 ms
    Memory:1560 kb
****************************************************************/

九度1008:最短路徑問題 題目地址:http://ac.jobdu.com/problem.php?pid=1008
題目描述:
給你n個點,m條無向邊,每條邊都有長度d和花費p,給你起點s終點t,要求輸出起點到終點的最短距離及其花費,如果最短距離有多條路線,則輸出花費最少的。
輸入:
輸入n,m,點的編號是1~n,然後是m行,每行4個數 a,b,d,p,表示a和b之間有一條邊,且其長度爲d,花費爲p。最後一行是兩個數 s,t;起點s,終點t。n和m爲0時輸入結束。
(1<n<=1000, 0<m<100000, s != t)
輸出:
輸出 一行有兩個數, 最短距離及其花費。
樣例輸入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
樣例輸出:
9 11
解題思路:也是求最短路徑,只要增加一個數組在記錄最短路徑的時候順便記錄一下最少花費,在路徑長度相等的情況下選擇花費比較少的路徑。

#include<iostream>
#include<cstring> 
using namespace std;
 
#define N 1001
int map[N][N];//鄰接矩陣 
int cost[N][N];//存儲每條邊對應的花費 
int used[N];//記錄是否已經計算出最短路徑 
int dis[N];//保存源點到任意點的最短距離 
int c[N];//保存源點到任意點的最少花費 
 
#define MAX 100000000
 
int main(){
    int n,m;
    while(cin >> n >> m){
        if(n == 0 && m == 0) break;
         
        //初始化鄰接矩陣map和花費cost 
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= n;j++){
                if(i == j) {
                    cost[i][j] = 0;
                    map[i][j] = 0;
                }
                else{
                    cost[i][j] = MAX;
                    map[i][j] = MAX;
                }
            }
        }
         
        memset(used,0,sizeof(used));
         
        //數據輸入 
        for(int i = 0;i < m;i++){
            int v1,v2,dis,c;
            cin >> v1 >> v2 >> dis >> c;
            map[v1][v2] = map[v2][v1] = dis;
            cost[v1][v2] = cost[v2][v2] = c;
        }
         
        //輸入源點和目的點 
        int start,end;
        cin >> start >> end;
         
        //dijkstra算法初始化 在記錄距離dis的同時還要記錄花費c 
        for(int i = 1;i <= n;i++){
            c[i] = cost[start][i];
            dis[i] = map[start][i];
        }
        used[start] = 1;//源點記錄爲已訪問 
         
        for(int i = 1;i <= n;i++){
            int min = MAX;
            int k;
             
            for(int j = 1;j <= n;j++){
                if(used[j] == 0&&dis[j] <= min){
                    min = dis[j];
                    k = j;
                }   
            }
            used[k] = 1;
            for(int j = 1;j <= n;j++){
                if(dis[j] > dis[k] + map[k][j]){
                    dis[j] = dis[k] + map[k][j];
                    c[j] = c[k] + cost[k][j];
                }
                else if(dis[j] == dis[k] + map[k][j]){
                    if(c[j] > c[k] + cost[k][j]){
                        c[j] = c[k] + cost[k][j];
                    }
                }
            }
             
        }
        cout << dis[end] << " " << c[end] << endl;
    }
     
    return 0;
} 
 
/**************************************************************
    Problem: 1008
    User: cherish
    Language: C++
    Result: Accepted
    Time:20 ms
    Memory:9360 kb
****************************************************************/

九度1162:I wanna go home 題目地址:http://ac.jobdu.com/problem.php?pid=1162
題目描述:

    The country is facing a terrible civil war----cities in the country are divided into two parts supporting different leaders. As a merchant, Mr. M does not pay attention to politics but he actually knows the severe situation, and your task is to help him reach home as soon as possible. 
    "For the sake of safety,", said Mr.M, "your route should contain at most 1 road which connects two cities of different camp."
    Would you please tell Mr. M at least how long will it take to reach his sweet home?

輸入:

    The input contains multiple test cases.
    The first line of each case is an integer N (2<=N<=600), representing the number of cities in the country.
    The second line contains one integer M (0<=M<=10000), which is the number of roads.
    The following M lines are the information of the roads. Each line contains three integers A, B and T, which means the road between city A and city B will cost time T. T is in the range of [1,500].
    Next part contains N integers, which are either 1 or 2. The i-th integer shows the supporting leader of city i. 
    To simplify the problem, we assume that Mr. M starts from city 1 and his target is city 2. City 1 always supports leader 1 while city 2 is at the same side of leader 2. 
    Note that all roads are bidirectional and there is at most 1 road between two cities.
Input is ended with a case of N=0.

輸出:

    For each test case, output one integer representing the minimum time to reach home.
    If it is impossible to reach home according to Mr. M's demands, output -1 instead.

樣例輸入:
2
1
1 2 100
1 2
3
3
1 2 100
1 3 40
2 3 50
1 2 1
5
5
3 1 200
5 3 150
2 5 160
4 3 170
4 2 170
1 2 2 2 1
0
樣例輸出:
100
90
540
解題思路:因爲Mr.M不想再兩個陣營之間來回穿梭,但是出發城市歸屬於陣營1,而目的城市歸屬於陣營2,所以只要把所有從陣營2到陣營1的道路設置爲不可達,這樣就又回到一般的求最短路徑的問題了。

#include<iostream>
using namespace std;
 
#define N 610
int map[N][N];
int dis[N];//用來記錄從源點到該點的最短路
int leader[N];//記錄城市i歸屬的陣營 
int used[N];//記錄該城市是否已經計算過最短路徑 
 
int main(){
    int n,m;
    while(cin >> n){
        if(n == 0) break;
        cin >> m;
         
        //鄰接矩陣初始化
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= n;j++){
                if(i == j) map[i][j] = 0;
                else map[i][j] = -1;
            }
            used[i] = 0;
        } 
         
        for(int i = 0;i < m;i++){
            int a,b,t;
            cin >> a >> b >> t;
            map[a][b] = map[b][a] = t;
        }
        for(int i = 1;i <= n;i++) cin >> leader[i];
         
        //因爲Mr.M不想在兩個陣營之間來回穿梭,但是出發城市歸屬於陣營1而目的城市歸屬於陣營2
        //所以要把所有從陣營2到陣營1的道路設爲不可達
        for(int i = 1;i <= n;i++){
            if(leader[i] == 2){
                for(int j = 1;j <= n;j++){
                    if(leader[j] == 1) map[i][j] = -1;
                }
            }
            dis[i] = map[1][i];//dijsktra算法初始化
        }
         
        used[1] = 1;
        for(int i =1;i <= n;i++){
            int min = 123123123;
            int k;
            for(int j = 1;j <= n;j++)
                if(used[j] == 0 && dis[j] != -1 && dis[j] <= min){
                    min = dis[j];
                    k = j;
                }
             
            used[k] = 1;
            for(int j = 1;j <= n;j++){
                if(map[k][j] != -1 && (dis[k] + map[k][j] < dis[j] || dis[j] == -1)){
                    dis[j] = dis[k] + map[k][j];
                }
            }
        }
        cout << dis[2] << endl;
    }
     
    return 0;
}
 
/**************************************************************
    Problem: 1162
    User: cherish
    Language: C++
    Result: Accepted
    Time:20 ms
    Memory:2980 kb
****************************************************************/

總結:Floyd算法的時間複雜度是O(n3),在一般允許的時間複雜度範圍內可以計算大約200個節點的圖;
Floyd算法能夠算出任意兩點之間的最短路徑,適用於求多個節點之間的最短路徑問題;Floyd算法用鄰接矩陣表示圖。
Dijkstra算法適用於求單源最短路徑,可以採用鄰接矩陣存儲圖也可以採用鄰接表表示圖,視情況而定。

發佈了47 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章