【模板】最大匹配
阿新 • • 發佈:2020-08-07
二分圖匹配
匈牙利演算法
- 時間複雜度\(O(nm)\)
#include <stdio.h> #include <string.h> #include <iostream> #include <queue> using namespace std; int mp[2100][2100]; // 圖的儲存矩陣 int n, m; int ans; bool vis[2100]; // 當前搜尋過程中是否被訪問過 int link[2100]; // y集合中的點在x集合中的匹配點 -1表示未匹配 bool find_(int x) { for (int i=1; i<=n; ++i) { if (mp[x][i] && !vis[i]) { // 有邊相連 vis[i] = 1; // 標記該點 if (link[i] == -1 || find_(link[i])) { //該點未匹配 或者匹配的點能找到增光路 link[i] = x; // 刪掉偶數條邊 加進奇數條邊 return true; // 找到增光路 } } } return false; } void match() { //初始化 ans = 0; memset(link, -1, sizeof(link)); for (int i=1; i<=n; ++i) { memset(vis, 0, sizeof(vis)); // 從集合的每個點依次搜尋 if (find_(i)) // 如果能搜尋到 匹配數加1 ans++; } return; } int main() { while(cin >> n >> m) { memset(mp, 0, sizeof(mp)); for (int i=0; i<m; ++i) { int x, y; cin >> x >> y; mp[x][y] = 1; mp[y][x] = 1; } //判斷是不是二分圖 過 match(); cout << ans/2 << endl; } return 0; }
HK演算法
-
時間複雜度為\(O(m\sqrt n)\)
-
點的序號要從0開始!
-
需要把nx,ny都賦值為n(點數)
const int MAXN = 1010; const int MAXM = 1010*1010; struct Edge { int v; int next; } edge[MAXM]; struct node { double x, y; double v; } a[MAXN], b[MAXN]; int nx, ny; int cnt; int t; int dis; int first[MAXN]; int xlink[MAXN], ylink[MAXN]; /*xlink[i]表示左集合頂點所匹配的右集合頂點序號,ylink[i]表示右集合i頂點匹配到的左集合頂點序號。*/ int dx[MAXN], dy[MAXN]; /*dx[i]表示左集合i頂點的距離編號,dy[i]表示右集合i頂點的距離編號*/ int vis[MAXN]; //尋找增廣路的標記陣列 void init() { cnt = 0; memset(first, -1, sizeof(first)); memset(xlink, -1, sizeof(xlink)); memset(ylink, -1, sizeof(ylink)); } void read_graph(int u, int v) { edge[cnt].v = v; edge[cnt].next = first[u], first[u] = cnt++; } int bfs() { queue<int> q; dis = INF; memset(dx, -1, sizeof(dx)); memset(dy, -1, sizeof(dy)); for(int i = 0; i < nx; i++) { if(xlink[i] == -1) { q.push(i); dx[i] = 0; } } while(!q.empty()) { int u = q.front(); q.pop(); if(dx[u] > dis) break; for(int e = first[u]; e != -1; e = edge[e].next) { int v = edge[e].v; if(dy[v] == -1) { dy[v] = dx[u] + 1; if(ylink[v] == -1) dis = dy[v]; else { dx[ylink[v]] = dy[v]+1; q.push(ylink[v]); } } } } return dis != INF; } int find(int u) { for(int e = first[u]; e != -1; e = edge[e].next) { int v = edge[e].v; if(!vis[v] && dy[v] == dx[u]+1) { vis[v] = 1; if(ylink[v] != -1 && dy[v] == dis) continue; if(ylink[v] == -1 || find(ylink[v])) { xlink[u] = v, ylink[v] = u; return 1; } } } return 0; } int MaxMatch() { int ans = 0; while(bfs()) { memset(vis, 0, sizeof(vis)); for(int i = 0; i < nx; i++) if(xlink[i] == -1) { ans += find(i); } } return ans; }
呼叫:
init(); for(int i = 0; i < m; i++) { if(l[edgee[i][0]] && edgee[i][1] != s && !l[edgee[i][1]]) read_graph(edgee[i][0],edgee[i][1]); if(l[edgee[i][1]] && edgee[i][0] != s && !l[edgee[i][0]]) read_graph(edgee[i][1],edgee[i][0]); } nx = n; ny = n; int ans = MaxMatch();
一般圖匹配(帶花樹)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1050;
bool g[maxn][maxn],inque[maxn],inpath[maxn];
bool inhua[maxn];
int st,ed,newbase,ans,n;
int base[maxn],pre[maxn],match[maxn];
int head,tail,que[maxn];
int x[maxn],y[maxn],f[maxn],mp[maxn][maxn],ne,np;
void Push(int u)
{
que[tail]=u;
tail++;
inque[u]=1;
}
int Pop()
{
int res=que[head];
head++;
return res;
}
int lca(int u,int v)//尋找公共花祖先
{
memset(inpath,0,sizeof(inpath));
while(1)
{
u=base[u];
inpath[u]=1;
if(u==st) break;
u=pre[match[u]];
}
while(1)
{
v=base[v];
if(inpath[v]) break;
v=pre[match[v]];
}
return v;
}
void reset(int u)//縮環
{
int v;
while(base[u]!=newbase)
{
v=match[u];
inhua[base[u]]=inhua[base[v]]=1;
u=pre[v];
if(base[u]!=newbase) pre[u]=v;
}
}
void contract(int u,int v)//
{
newbase=lca(u,v);
memset(inhua,0,sizeof(inhua));
reset(u);
reset(v);
if(base[u]!=newbase) pre[u]=v;
if(base[v]!=newbase) pre[v]=u;
for(int i=1;i<=n;i++)
{
if(inhua[base[i]]){
base[i]=newbase;
if(!inque[i])
Push(i);
}
}
}
void findaug()
{
memset(inque,0,sizeof(inque));
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)//並查集
base[i]=i;
head=tail=1;
Push(st);
ed=0;
while(head<tail)
{
int u=Pop();
for(int v=1;v<=n;v++)
{
if(g[u][v]&&(base[u]!=base[v])&&match[u]!=v)
{
if(v==st||(match[v]>0)&&pre[match[v]]>0)//成環
contract(u,v);
else if(pre[v]==0)
{
pre[v]=u;
if(match[v]>0)
Push(match[v]);
else//找到增廣路
{
ed=v;
return ;
}
}
}
}
}
}
void aug()
{
int u,v,w;
u=ed;
while(u>0)
{
v=pre[u];
w=match[v];
match[v]=u;
match[u]=v;
u=w;
}
}
void edmonds()//匹配
{
memset(match,0,sizeof(match));
for(int u=1;u<=n;u++)
{
if(match[u]==0)
{
st=u;
findaug();//以st開始尋找增廣路
if(ed>0) aug();//找到增廣路 重新染色,反向
}
}
}
//以上是帶花樹求最大匹配演算法 不用看
void create()//建圖
{
n=0;
memset(g,0,sizeof(g));
for(int i=1;i<=np;i++)
for(int j=1;j<=f[i];j++)
mp[i][j]=++n;//拆點,給每個度的點編號
for(int i=0;i<ne;i++)
{//此時n+1代表x,n+2代表y
for(int j=1;j<=f[x[i]];j++)
g[mp[x[i]][j]][n+1]=g[n+1][mp[x[i]][j]]=1;//每個度的點與對應的x,y相連
for(int j=1;j<=f[y[i]];j++)
g[mp[y[i]][j]][n+2]=g[n+2][mp[y[i]][j]]=1;
g[n+1][n+2]=g[n+2][n+1]=1;//x與y相連
n+=2;
}
}
void print()
{
ans=0;
for(int i=1;i<=n;i++)
if(match[i]!=0)
{
ans++;
// if(match[i]>i)
// cout<<"_____"<<i<<' '<<match[i]<<endl;
}
//cout<<"******"<<ans<<' '<<n<<endl;
if(ans==n) printf("YES\n");
else printf("NO\n");
}
int main()
{
int t,k=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&np,&ne);
for(int i=0;i<ne;i++)
scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=np;i++)
scanf("%d",&f[i]);
printf("Case %d: ",++k);
create();
edmonds();
print();
}
return 0;
}