Floyd打印最短路徑及選址問題

前言:在求任意兩點間的最短路問題中,圖一般較爲稠密,使用Floyd算法可以在O(N ^ 3)的時間實現。當然也可以把每個點作爲起點,求解N次單源最短路徑問題,但較爲複雜。這裏介紹Floyd算法以及使用Floyd算法打印路徑和解決選址問題

算法分析

假設用d[k, i, j]表示“經過若干編號不超過k的結點”從i 到 j的最短路長度,這個問題可以劃分爲兩個子問題,經過編號不超過 k - 1 的結點從i到j或者從i 先到k ,再到j,所以有:
d[k, i, j] = min(d[k - 1, i, j], d[k - 1, i, k] + d[k - 1, k, j])

因爲一般解決稠密圖,所以使用鄰接矩陣。

Floyd算法的本質是動態規劃,k表示我們所劃分的階段,所以在循環的最外層,但我們寫的時候已經將其優化,像揹包問題等動態規劃中那樣,使用“滾動數組”優化。

所以纔有: d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);

Floyd裸模板

給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環,邊權可能爲負數。

再給定k個詢問,每個詢問包含兩個整數x和y,表示查詢從點x到點y的最短距離,如果路徑不存在,則輸出“impossible”。

數據保證圖中不存在負權迴路。

輸入格式

第一行包含三個整數n,m,k

接下來m行,每行包含三個整數x,y,z,表示存在一條從點x到點y的有向邊,邊長爲z。

接下來k行,每行包含兩個整數x,y,表示詢問點x到點y的最短距離。

輸出格式

共k行,每行輸出一個整數,表示詢問的結果,若詢問兩點間不存在路徑,則輸出“impossible”。

數據範圍

1≤n≤200,
1 ≤ k ≤ n2
1 ≤ m ≤20000

圖中涉及邊長絕對值均不超過10000。

輸入樣例:

3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3

輸出樣例:

impossible
1
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 210, INF = 1e9;
int n, m, q;
int d[N][N];
void flody()
{
    for(int k = 1; k <= n; k ++ )
        for(int i = 1; i <= n; i ++ )
            for(int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
}
int main()
{
    cin >> n >> m >> q;
    
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= n; j ++ )
            if(i == j) d[i][j] = 0;
            else d[i][j] = INF;
            
    while(m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        d[a][b] = min(d[a][b], c);
    }
    
    flody();
    
    while(q -- )
    {
        int x, y;
        cin >> x >> y;
        
        int t = d[x][y];
        
        if(t > INF / 2) puts("impossible");
        else cout << t << endl;
    }
    
    return 0;
}

Floyd打印最短路徑

path[i][j]數組存儲 i ~ j這條路上 j 前面那個點,假設爲k,i ~ j 拆開就是i~ k 和 k ~ j,可以求出k前面的點,遞歸將i ~ j上經過的所有點全部打印出來。

樣例:

4 5
1 2 1
1 3 4
2 3 1
3 4 1
1 4 6
1 4

輸出:

1 -> 4的最短路徑爲:1 2 3 4
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 210, INF = 1e6;
int n, m;
int d[N][N],path[N][N];
void init();
void floyd();
void printpath(int a, int b);
int main()
{
	memset(d, 0x3f, sizeof d);
	cout << "請輸入點、邊的數量:" << endl; 
	cin >> n >> m;
	init();
	
	cout << "請輸入邊的信息:" << endl; 
	for(int i = 0; i < m; i ++ )
	{
		int a, b, c;
		cin >> a >> b >> c;
		d[a][b] = min(d[a][b], c);
	}
	
	floyd();
	
	cout << "請輸入查詢路徑:" << endl;
	int x, y; 
	cin >> x >> y;
	printf("%d -> %d的最短路徑爲:", x, y); 
	
	printpath(x, y);
	
	cout << endl;
	
	return 0;
}
void init()
{ 
	for(int i = 1; i <= n; i ++ )
		for(int j = 1; j <= n; j ++ )
		{
			if(i == j) d[i][j] = 0;
			else d[i][j] = INF;
			
			path[i][j] = i;
		}			
}
void floyd()
{
	for(int k = 1; k <= n; k ++ )
		for(int i = 1; i <= n; i ++ )
			for(int j = 1; j <= n ; j++ )
			{
				if(d[i][j] > d[i][k] + d[k][j])
				{
					d[i][j] = d[i][k] + d[k][j]; // 從i ~ j 的最短路長度 
					path[i][j] = k;
				}
				
			}
}
void printpath(int a, int b)
{
	if(a == b) 
	{
		cout << a << " ";
		return ;
	}
	int k = path[a][b];
	printpath(a, k);		
	cout << b << " ";
}

選址問題

比如建造一個加油站,使所有村莊到加油站的距離最近,問加油站應該建在哪裏。
通過Floyd計算出每個點到所有點的最短距離總和,找到總和最小點的就是加油站的地址。

樣例可以自己模擬一個:
第一行輸入點數n和邊數m
然後m行依次輸入每個點的信息

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 210, INF = 1e6;
int n, m;
int d[N][N];
void floyd();
int main()
{
    cin >> n >> m;
    
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= n; j ++ )
            if(i == j) d[i][j] = 0;
            else d[i][j] = INF;
            
    while(m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        d[a][b] = min(d[a][b], c);
    }
    
    floyd();
    
	int ans = INF;			//存路經總和 
	int location = 0;			//加油站位置 
	
	//確定加油站的位置 
	for(int i = 1; i <= n; i ++ )
	{
		int sum = 0;		//用來更新每個點到其他所有點的距離之和 
		for(int j = 1; j <= n; j ++ )
			sum += d[i][j];	//將此點到所有村莊的路程加起來 
		
		//求出路徑之和加起來最短的 
		if(sum < ans) 
		{
			ans = sum;
			location = i;		//i即爲醫院所在位置 
		}
	} 	
	cout << "位置及最短路程總和爲:"; 
	cout << location << " " << ans; 
    
    return 0;
}
void floyd()
{
    for(int k = 1; k <= n; k ++ )
        for(int i = 1; i <= n; i ++ )
            for(int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章