图论--一般图带花树匹配--模板
生活随笔
收集整理的這篇文章主要介紹了
图论--一般图带花树匹配--模板
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
#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的区别
- 下一篇: 图论--二分图最大匹配(匈牙利算法)--