微信公眾號開啟伺服器配置 JAVA
阿新 • • 發佈:2020-12-22
應用
- 求多源點最短路
- 傳遞閉包
- 找最小環(對於正權圖而言)
- 恰好經過k條邊的最短路
floyd演算法原理
演算法模板
//初始化:d[i][i] = 0 且不相連的節點距離需要初始化為INF
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
空間優化
- 需要說明表示式
d[k][i][j] = min(d[k][i][j] , d[k-1][i][k] + d[k-1][k][j])
d[i][j] = min(d[i][j] , d[i][k] + d[k][j])
等價 - 以
d[k-1][i][k] <=> d[i][k]
為例,只需要說明每一次d[i][k]
的值可能被更新時,d[i][k]
的值不變 - 令
j=k
, 則d[k][i][k] = min(d[k][i][k] , d[k-1][i][k] + d[k-1][k][k])
.因為d[k-1][k][k]=0
,所以d[i][k]
的值不會發生改變,總是第k-1
層的值
例題-牛的旅行
題解
1.根據輸入建立鄰接矩陣d
,跑一邊floyd演算法
2.求每一個連通塊的直徑(距離最遠的兩點的距離),答案必定>=每一連通塊的直徑
3.列舉連線兩個連通塊內的某點所形成的新連通塊的直徑中的最小值,答案必定>=這個最小值
4.新的連通塊直徑(例如連線i,j兩點):maxd[i] + dist(i , j) + maxd[j]
程式碼
#include <iostream> #include <cstring> #include <cmath> using namespace std; const int N = 160; const double INF = 1e20; #define x first #define y second typedef pair<int ,int> PII; PII p[N]; double d[N][N] , maxd[N]; char g[N][N]; int n; double get_dist(PII a , PII b) { double x = a.x - b.x , y = a.y - b.y; return sqrt(x*x + y*y); } int main() { cin >> n; for(int i = 0; i < n; i++) cin >> p[i].x >> p[i].y; for(int i = 0; i < n; i++) cin >> g[i]; //初始化距離 for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) if(i != j) { if(g[i][j] == '1') d[i][j] = get_dist(p[i] , p[j]); else d[i][j] = INF; } //Floyd演算法 for(int k = 0; k < n; k++) for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) d[i][j] = min(d[i][j] , d[i][k] + d[k][j]); //求連通塊內的直徑 for(int i = 0; i < n; i++) for(int j = 0;j < n; j++) if(d[i][j] < INF) maxd[i] = max(maxd[i] , d[i][j]); //情況1:結果必然 >= 每一連通塊的直徑 double ans = 0; for(int i = 0; i < n; i++) ans = max(ans , maxd[i]); //情況2:結果必然 >= 連線一條邊後新的連通塊的直徑(列舉最小值) double res = INF; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) if(d[i][j] >= INF) res = min(res , maxd[i] + get_dist(p[i] , p[j]) + maxd[j]); printf("%lf\n" , max(ans , res)); return 0; }
例題-排序(傳遞閉包)
floyd演算法求傳遞閉包
- 1.鄰接矩陣初始化d[i][j]:1-表示i,j之間存在i到j的單向路徑 0-表示不存在路徑
- 2.將轉移關係修改為:d[i][j] = d[i][j] | ( d[i][k] & d[k][j] ) -(表示 i->k , k->j兩段路徑都存在)
- 3.整體認識:在演算法結束後,所有的間接關係,例如 i->j , j->k,會直接表示出來,即d[i][k] = 1
題解
- 在本題中可以將A < B類比成A->B
- 則存在矛盾例如A<B , B<C , C<A , 可以推知A<A , 即A->A , d[i][i] = 1
- 變數A,B不存在相對關係:d[a][b] = 0 && d[b][a] = 0
程式碼
#include <iostream>
#include <cstring>
using namespace std;
const int N = 30;
int d[N][N] , g[N][N];
int n , m;
void floyd()
{
memcpy(d , g , sizeof g);
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j ++)
d[i][j] |= d[i][k] & d[k][j];
}
int check()
{
for(int i = 0; i < n; i++)
if(d[i][i]) return 2;
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
if(!d[i][j] && !d[j][i]) return 0;
return 1;
}
char get_min(int val)
{
for(int i = 0; i < n; i++)
{
int cnt = 0;
for(int j = 0; j < n; j++)
if(d[i][j]) cnt++;
if(cnt == val) return i + 'A';
}
}
int main()
{
while(cin >> n >> m , n || m)
{
char str[4];
//type: 0-表示存在無法判斷的關係 1-表示所有關係已確定 2-關係存在矛盾
int type = 0 , t;
memset(g , 0 , sizeof g);
for(int i = 1; i <= m; i++)
{
cin >> str;
int a = str[0] - 'A' , b = str[2] - 'A';
if(!type)
{
g[a][b] = 1;
floyd();
type = check();
if(type) t = i;
}
}
if(!type) printf("Sorted sequence cannot be determined.\n");
else if(type == 2) printf("Inconsistency found after %d relations.\n" , t);
else
{
printf("Sorted sequence determined after %d relations: " , t);
for(int i = 0; i < n; i++)
printf("%c" , get_min(n - i - 1));
puts(".");
}
}
return 0;
}
參考文獻
Acwing-演算法提高課-圖論章節
https://www.acwing.com/activity/content/introduction/16/