【UOJ】#79. 一般圖最大匹配
阿新 • • 發佈:2018-06-11
當前 size define AI for while 怎麽 mem AS
題解
板子!我相信其實沒人來看我的板子!但是為了防止我忘記,我還是要寫點什麽
我們考慮二分圖,為什麽二分圖就能那麽輕松地寫出匹配的代碼呢?因為匹配只會發生在黑點和白點之間,我們找尋增廣路,必然是一黑一白一黑一白這麽走
然而,一般圖由於有了奇環,事情變得不妙了啊
奇環上的所有點,可以是……任意的奇偶性(起點到它的距離的奇偶性,可以是非簡單路徑)
那麽我們就讓任意奇偶性的點可以進行匹配就可以了,我們通過pre維護出一條路徑到達根節點
怎麽維護呢?縮花!
花?什麽是花?
花就是奇環,我們找到花托(兩個點的最近公共祖先),從兩個點構建一條能走到花托的路徑
如何構建
對於(u,v)和花托f
我們讓u的pre走到v,然後令u跳到u匹配點的pre,v變成u的匹配點,繼續這個操作,同時把這個匹配點改成偶點(可進行增廣)
對v走到花托走到f進行類似的操作
代碼
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define enter putchar(‘\n‘)
#define space putchar(‘ ‘)
#define MAXN 505
//#define ivorysi
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < ‘0‘ || c > ‘9‘) {
if(c == ‘-‘) f = -1;
c = getchar();
}
while(c >= ‘0‘ && c <= ‘9‘) {
res = res * 10 + c - ‘0‘;
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {putchar(‘-‘ );x = -x;}
if(x >= 10) {
out(x / 10);
}
putchar(‘0‘ + x % 10);
}
struct node {
int to,next;
}E[MAXN * MAXN * 2];
int sumE,head[MAXN];
int N,M,fa[MAXN],pre[MAXN],matk[MAXN],sta[MAXN];
int Q[MAXN],L,R;
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
int getfa(int x) {
return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
int lca(int x,int y) {
static int vis[MAXN],Tim;
++Tim;
while(1) {//每個點都嘗試走一步
x = getfa(x);
if(x) {
if(vis[x] == Tim) {
return x;
}
else vis[x] = Tim,x = pre[matk[x]];
}
swap(x,y);
}
}
void blossom(int u,int v,int flower_root) {
while(getfa(u) != flower_root) {
pre[u] = v;
//我們每個點既然都是奇偶難分(霧)
//就構建出一條這個點走到花托的交錯路
//構建方法就是順著匹配邊往上跳
//這個時候我們碰到的匹配點還都以為自己是偶點= =(奇怪的敘述)
int m = matk[u];
if(sta[m] == 1) {sta[m] = 0;Q[++R] = m;}
//把所有以為自己是奇點的人告訴他們可以當偶點啦
if(u == fa[u]) fa[u] = flower_root;
if(m == fa[m]) fa[m] = flower_root;
v = m,u = pre[m];
}
}
bool match(int s) {
//sta : -1 未訪問 0:偶點(起始點) 1:奇點
//pre : 交錯樹上的父親,任意一點順著當前自己的匹配點的pre會走到樹根,也就是還原出了交錯路
L = 1,R = 0;Q[++R] = s;
memset(sta,-1,sizeof(sta));
memset(pre,0,sizeof(pre));
sta[s] = 0;//起點是偶點
for(int i = 1 ; i <= N ; ++i) fa[i] = i;
while(L <= R) {
int u = Q[L++];
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(sta[v] == -1) {
pre[v] = u;sta[v] = 1;
if(!matk[v]) {
for(int j = u , k = v,last; ; j = pre[k = last]) {
last = matk[j],matk[j] = k;matk[k] = j;
if(!last) break;
}//順著pre節點找交錯路,全部取反
return 1;
}
sta[matk[v]] = 0;
Q[++R] = matk[v];
}
else if(getfa(v) != getfa(u) && !sta[v]) {
int f = lca(u,v);blossom(u,v,f);blossom(v,u,f);
//兩個偶點,縮花,兩條路徑爬到父親
}
}
}
return 0;
}
void Init() {
read(N);read(M);
int u,v;
for(int i = 1 ; i <= M ; ++i) {
read(u);read(v);
add(u,v);add(v,u);
}
}
void Solve() {
int ans = 0;
for(int i = 1 ; i <= N ; ++i) {
if(!matk[i] && match(i)) ++ans;
}
out(ans);enter;
for(int i = 1 ; i <= N ; ++i) {
out(matk[i]);
if(i == N) enter;
else space;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
}
//5aaC5p6c5oiR5LiK5LiN5LqGdGh177yM5oiR5bCx5YaN5Lmf6KeB5LiN5Yiw54yr6ZSf5LqG
//4oCm4oCm54yr6ZSf55yf5Y+v54ix
【UOJ】#79. 一般圖最大匹配