1. 程式人生 > >【UOJ】#79. 一般圖最大匹配

【UOJ】#79. 一般圖最大匹配

當前 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. 一般圖最大匹配