AcWing 344. 觀光之旅
阿新 • • 發佈:2022-03-21
一、Floyd求最小環
(\(Floyd\)) \(O(n^3)\)
設環的形式是:\(i<->k<->j\) , \(i<–>j\) (i\(,j,k\)不同)
\(floyd\)是典型的插點演算法,每次插入點\(k\),為此,在點\(k\)被[插入前]可計算\(i->j->k\)這個環
即此時中間節點為:\(1~k-1\),即我們已經算出了任意\(i<->j\)的最短道路,中間經過的節點可以為 \((1,2,3,…,k-1)\)
我們只需列舉所有以\(k\)為環中最大節點的環即可。
\(pos[i][j]:i~j\)的最短路中經過的點是\(k\)
這條道路存在以下兩條性質
1.在\(i~j\)的最短道路中,一定沒有環(顯然)
2.設\(i,j\)之間的最短道路經過點\(k\)(不同於\(i,j\)),則\(i~k , k~j\)之間必然沒有交集
二、實現程式碼
#include <bits/stdc++.h> using namespace std; // 使用Floyd求最小環的經典題 const int N = 110; //稠密圖,最多100個頂點,10000條邊 const int INF = 0x3f3f3f3f; typedef long long LL; LL res = INF; //最小環的長度,因為是多條路徑的和,可能大於INT_MAX int g[N][N]; //鄰接矩陣,儲存任意兩點間的 "直接距離" int d[N][N]; //鄰接矩陣,儲存任意兩點間的 "最短距離" int pos[N][N]; //記錄i~j的最短距離是由k做為中間點獲取到的 vector<int> path; //最小環的組成點有哪些 int n, m; // n行m列的二維矩陣 // i->j之間的路,輸出i到j之間不包括i和j的道路 void dfs(int i, int j) { int k = pos[i][j]; // 中間轉移點 if (k == 0) return; // 沒有轉移點返回 dfs(i, k); // 遞迴計算i~k之間的路徑 path.push_back(k); // k dfs(k, j); // 遞迴計算k~j之間的路徑 } //獲取 i->k->j-->i的環路徑 void get_path(int i, int j, int k) { path.clear(); path.push_back(k); //邊界 path.push_back(i); dfs(i, j); // k->i-->j->k path.push_back(j); } int main() { cin >> n >> m; // n個點,m條邊 memset(g, 0x3f, sizeof g); //任意兩點間距離正無窮 for (int i = 0; i < n; i++) g[i][i] = 0; //自己和自己是距離為0的 int a, b, c; while (m--) { cin >> a >> b >> c; g[a][b] = g[b][a] = min(g[a][b], c); //保留最短邊 } memcpy(d, g, sizeof d); //原圖 for (int k = 1; k <= n; k++) { //至少包含三個點的環所經過的點的最大編號是k //當列舉到k時,其實現在g陣列中儲存的是前k-1條邊時的多原最短路徑 //如果引入k點,則可能的環路徑長度為g[i][j] + d[i][k] + d[k][j] // d:直接路徑 // g:最短路徑長度(經過Floyd計算過的最短路徑) for (int i = 1; i < k; i++) //至少包含三個點,i,j,k不重合 for (int j = i + 1; j < k; j++) if (res > (LL)g[i][j] + d[i][k] + d[k][j]) { //最小環 res = g[i][j] + d[i][k] + d[k][j]; get_path(i, j, k); // i->k->-j-->i為最小環,需要記錄一下路徑 } //原始版本Floyd for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (g[i][j] > g[i][k] + g[k][j]) { g[i][j] = g[i][k] + g[k][j]; pos[i][j] = k; //這裡比原始版本Floyd多了一步記錄任意兩點是通過k進行轉移的 } } //如果沒有被修改過,則返回不存在最小環 if (res == INF) cout << "No solution." << endl; else { //輸出路徑 for (auto x : path) cout << x << ' '; cout << endl; } return 0; }