图论--一般带花树匹配
生活随笔
收集整理的這篇文章主要介紹了
图论--一般带花树匹配
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
帶花樹就是說一個非二分圖,圖中帶有奇環的圖,我們不能在奇環中找增廣路,因為會陷入死循環,我們可以將帶花樹的花(奇環)部分縮成點處理,剩下的圖就是一個無奇環的圖。我們再找增廣路,而奇環中的的點我們可以隨意分配,但是說起來簡單,但是實現很難。經過前人的探索,還有這篇《Efficient Algorithms for Finding Maximal Matching in Graphs》論文,呃,然后后人就寫出來模板,這就是一個模板題。
模板:
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int N = 250; // 并查集維護 int belong[N]; int findb(int x) {return belong[x] == x ? x : belong[x] = findb(belong[x]); } void unit(int a, int b) {a = findb(a);b = findb(b);if (a != b) belong[a] = b; }int n, match[N]; vector<int> e[N]; int Q[N], rear; int _next[N], mark[N], vis[N]; // 樸素算法求某階段中搜索樹上兩點x, y的最近公共祖先r int LCA(int x, int y) {static int t = 0; t++;while (true) {if (x != -1) {x = findb(x); // 點要對應到對應的花上去 if (vis[x] == t)return x;vis[x] = t;if (match[x] != -1)x = _next[match[x]];else x = -1;}swap(x, y);} }void group(int a, int p) {while (a != p) {int b = match[a], c = _next[b];// _next數組是用來標記花朵中的路徑的,綜合match數組來用,實際上形成了 // 雙向鏈表,如(x, y)是匹配的,_next[x]和_next[y]就可以指兩個方向了。 if (findb(c) != p) _next[c] = b;// 奇環中的點都有機會向環外找到匹配,所以都要標記成S型點加到隊列中去, // 因環內的匹配數已飽和,因此這些點最多只允許匹配成功一個點,在aug中 // 每次匹配到一個點就break終止了當前階段的搜索,并且下階段的標記是重 // 新來過的,這樣做就是為了保證這一點。 if (mark[b] == 2) mark[Q[rear++] = b] = 1;if (mark[c] == 2) mark[Q[rear++] = c] = 1;unit(a, b); unit(b, c);a = c;} }// 增廣 void aug(int s) {for (int i = 0; i < n; i++) // 每個階段都要重新標記 _next[i] = -1, belong[i] = i, mark[i] = 0, vis[i] = -1;mark[s] = 1;Q[0] = s; rear = 1;for (int front = 0; match[s] == -1 && front < rear; front++) {int x = Q[front]; // 隊列Q中的點都是S型的 for (int i = 0; i < (int)e[x].size(); i++) {int y = e[x][i];if (match[x] == y) continue; // x與y已匹配,忽略 if (findb(x) == findb(y)) continue; // x與y同在一朵花,忽略 if (mark[y] == 2) continue; // y是T型點,忽略 if (mark[y] == 1) { // y是S型點,奇環縮點 int r = LCA(x, y); // r為從i和j到s的路徑上的第一個公共節點 if (findb(x) != r) _next[x] = y; // r和x不在同一個花朵,_next標記花朵內路徑 if (findb(y) != r) _next[y] = x; // r和y不在同一個花朵,_next標記花朵內路徑 // 將整個r -- x - y --- r的奇環縮成點,r作為這個環的標記節點,相當于論文中的超級節點 group(x, r); // 縮路徑r --- x為點 group(y, r); // 縮路徑r --- y為點 }else if (match[y] == -1) { // y自由,可以增廣,R12規則處理 _next[y] = x;for (int u = y; u != -1; ) { // 交叉鏈取反 int v = _next[u];int mv = match[v];match[v] = u, match[u] = v;u = mv;}break; // 搜索成功,退出循環將進入下一階段 }else { // 當前搜索的交叉鏈+y+match[y]形成新的交叉鏈,將match[y]加入隊列作為待搜節點 _next[y] = x;mark[Q[rear++] = match[y]] = 1; // match[y]也是S型的 mark[y] = 2; // y標記成T型 }}} }bool g[N][N]; int main() {scanf("%d", &n);for (int i = 0; i < n; i++)for (int j = 0; j < n; j++) g[i][j] = false;// 建圖,雙向邊 int x, y; while (scanf("%d%d", &x, &y) != EOF) {x--, y--;if (x != y && !g[x][y])e[x].push_back(y), e[y].push_back(x);g[x][y] = g[y][x] = true;}// 增廣匹配 for (int i = 0; i < n; i++) match[i] = -1;for (int i = 0; i < n; i++) if (match[i] == -1) aug(i);// 輸出答案 int tot = 0;for (int i = 0; i < n; i++) if (match[i] != -1) tot++;printf("%d\n", tot);for (int i = 0; i < n; i++) if (match[i] > i)printf("%d %d\n", i + 1, match[i] + 1);return 0; }?
總結
以上是生活随笔為你收集整理的图论--一般带花树匹配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 退市整理期股票交易规则
- 下一篇: ipad2和ipad air2的区别