二分图/一般图匹配专题

洛谷P1129

匈牙利算法 求二分图最大匹配

#include <bits/stdc++.h>
using namespace std;
const int MAX1=5*1e3+1;
const int MAX2=5*1e4+1;
struct Edge
{
    int to,next;
}edge[MAX2];
int head[MAX1],tot;
int linker[MAX1],n;
bool used[MAX1];
void read(int &x)
{
    x=0;int f=1;char s=getchar();
    while (s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while (s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
bool dfs(int u)
{
    for (int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!used[v])
        {
            used[v]=1;
            if(linker[v]==-1||dfs(linker[v]))
            {
                linker[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
int hungary()
{
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(int u=1;u<=n;u++)
    {
        memset(used,0,sizeof(used));
        if(dfs(u)) res++;//return 0;
    }
    return res;//return 1;
}
int main ()
{
    int t,a;
    read(t);
    while(t--)
    {
        init();
        read(n);
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                read(a);
                if(a==1) addedge(k,n+i);
            }
        }
        hungary()==n?printf("Yes\n"):printf("No\n");
    }
    return 0;
}

HDU3605 Escape

匈牙利算法 求二分图最大多重匹配

#include <bits/stdc++.h>
using namespace std;
const int MAX1=1e5+2;
const int MAX2=10+2;
int n,m;
int g[MAX1][MAX2];
int linker[MAX2][MAX1],vlink[MAX2];
bool used[MAX2];
int num[MAX2];
void read(int &x)
{
    x=0;int f=1;char s=getchar();
    while (s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while (s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
bool dfs(int u)
{
    for(int v=1;v<=m;v++)
    {
        if(g[u][v]&&!used[v])
        {
            used[v]=1;
            if(vlink[v]<num[v])
            {
                linker[v][++vlink[v]]=u;
                return 1;
            }
            for(int k=1;k<=vlink[v];k++)
            {
                if(dfs(linker[v][k]))
                {
                    linker[v][k]=u;
                    return 1;
                }
            }
        }
    }
    return 0;
}
int hungary()
{
    memset(linker,-1,sizeof(linker));
    memset(vlink,0,sizeof(vlink));
    for(int u=1;u<=n;u++)
    {
        memset(used,0,sizeof(used));
        if(!dfs(u)) return 0;
    }
    return 1;
}
int main()
{
    while (~scanf("%d%d",&n,&m))
    {
        memset(g,0,sizeof(g));
        for(int k=1;k<=n;k++)
            for(int i=1;i<=m;i++)
                read(g[k][i]);
        for(int k=1;k<=m;k++)
            read(num[k]);
        if (hungary()) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

HDU1533 Going Home

KM算法 求二分图最大权匹配

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int MAX=1e2+5;
const int INF=0x3f3f3f3f;
int n,nx,ny;
int linker[MAX],lx[MAX],ly[MAX],slack[MAX];
int visx[MAX],visy[MAX],w[MAX][MAX];
int dfs(int x)
{
    visx[x]=1;
    for(int y=1;y<=ny;y++)
    {
        if(visy[y]) continue;
        int tmp=lx[x]+ly[y]-w[x][y];
        if(tmp==0)
        {
            visy[y]=1;
            if(linker[y]==-1||dfs(linker[y]))
            {
                linker[y]=x;
                return 1;
            }
        }
        else if(slack[y]>tmp) slack[y]=tmp;
    }
    return 0;
}
int km()
{
    int i,j;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(i=1;i<=nx;i++)
        for(j=1,lx[i]=-INF;j<=ny;j++)
            if(w[i][j]>lx[i]) lx[i]=w[i][j];
    for(int x=1;x<=nx;x++)
    {
        for(i=1;i<=ny;i++) slack[i]=INF;
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(x)) break;
            int d=INF;
            for(i=1;i<=ny;i++)
                if(!visy[i] && d>slack[i]) d=slack[i];
            for(i=1;i<=nx;i++)
                if(visx[i]) lx[i]-=d;
            for(i=1;i<=ny;i++)
                if(visy[i]) ly[i]+=d;
                else slack[i]-=d;
        }
    }
    int res=0;
    for(i=1;i<=ny;i++)
        if(linker[i]!=-1) res+=w[linker[i]][i];
    return res;
}
int main()
{
    int n,m;
    char c;
    pair<int,int>a[MAX],b[MAX];
    int top1,top2;
    while(~scanf("%d%d",&n,&m)&&n&&m)
    {
        top1=top2=0;
        memset(w,0,sizeof(w));
        for(int k=0;k<n;k++)
        {
            c=getchar();
            for(int i=0;i<m;i++)
            {
                c=getchar();
                if(c=='H')
                    a[++top1]=make_pair(k,i);
                else if(c=='m')
                    b[++top2]=make_pair(k,i);
            }
        }
        for(int k=1;k<=top1;k++)
            for(int i=1;i<=top2;i++)
                w[k][i]=-abs(a[k].x-b[i].x)-abs(a[k].y-b[i].y);
        nx=top1;ny=top2;
        printf("%d\n",-km());
    }
    return 0;
}

UOJ#79 一般图最大匹配

带花树算法 求一般图最大匹配

//非本人代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=505;
const int maxm=maxn*maxn*2;
int n,m,que[maxm],ql,qr,pre[maxn],tim=0;
struct edge
{
    int v,nxt;
}e[maxm];
int h[maxn],tot;
int match[maxn],f[maxn],tp[maxn],tic[maxn];
void read(int &x)
{
    x=0;int f=1;char s=getchar();
    while (s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while (s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int find(int x)
{
    return f[x]==x?f[x]:f[x]=find(f[x]);
}
void init()
{
    memset(h,-1,sizeof(h));
    tot=0;
}
void add(int u,int v)
{
    e[++tot]=(edge){v,h[u]};
    h[u]=tot;
    e[++tot]=(edge){u,h[v]};
    h[v]=tot;
}
int lca(int x,int y)
{
    for (++tim;;swap(x,y)) if (x)
    {
        x=find(x);
        if (tic[x]==tim) return x;
        else tic[x]=tim,x=pre[match[x]];
    }
}
void shrink(int x,int y,int p)
{
    while (find(x)!=p)
    {
        pre[x]=y,y=match[x];
        if (tp[y]==2) tp[y]=1,que[++qr]=y;
        if (find(x)==x) f[x]=p;
        if (find(y)==y) f[y]=p;
        x=pre[y];
    }
}
bool aug(int s) 
{
    for (int i=1;i<=n;++i) f[i]=i;
    memset(tp,0,sizeof tp);
    memset(pre,0,sizeof pre);
    tp[que[ql=qr=1]=s]=1; // 1: type A ; 2: type B
    while (ql<=qr)
    {
        int x=que[ql++];
        for (int i=h[x];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            if (find(v)==find(x)||tp[v]==2) continue;
            if (!tp[v])
            {
                tp[v]=2,pre[v]=x;
                if (!match[v])
                {
                    for (int now=v,last,tmp;now;now=last)
                    {
                        last=match[tmp=pre[now]];
                        match[now]=tmp,match[tmp]=now;
                    }
                    return true;
                }
                tp[match[v]]=1,que[++qr]=match[v];
            }
            else if (tp[v]==1)
            {
                int l=lca(x,v);
                shrink(x,v,l);
                shrink(v,x,l);
            }
        }
    }
    return false;
}
int main()
{
    read(n),read(m);
    init();
    for (int i=1;i<=m;++i)
    {
        int x,y;
        read(x);read(y);
        add(x,y);
    }
    int ans=0;
    for (int i=1;i<=n;++i) ans+=(!match[i]&&aug(i));//最大匹配个数
    printf("%d\n",ans);
    for (int i=1;i<=n;++i) printf("%d ",match[i]);//match[i]为i的匹配
    return 0;
}

一般图最大权匹配(无例题)

//非本人代码
#include<bits/stdc++.h>
using namespace std;
//from vfleaking
//自己進行一些進行一些小修改 
#define INF INT_MAX
#define MAXN 400
struct edge{
	int u,v,w;
	edge(){}
	edge(int u,int v,int w):u(u),v(v),w(w){}
};
int n,n_x;
edge g[MAXN*2+1][MAXN*2+1];
int lab[MAXN*2+1];
int match[MAXN*2+1],slack[MAXN*2+1],st[MAXN*2+1],pa[MAXN*2+1];
int flower_from[MAXN*2+1][MAXN+1],S[MAXN*2+1],vis[MAXN*2+1];
vector<int> flower[MAXN*2+1];
queue<int> q;
inline int e_delta(const edge &e){ // does not work inside blossoms
	return lab[e.u]+lab[e.v]-g[e.u][e.v].w*2;
}
inline void update_slack(int u,int x){
	if(!slack[x]||e_delta(g[u][x])<e_delta(g[slack[x]][x]))slack[x]=u;
}
inline void set_slack(int x){
	slack[x]=0;
	for(int u=1;u<=n;++u)
		if(g[u][x].w>0&&st[u]!=x&&S[st[u]]==0)update_slack(u,x);
}
void q_push(int x){
	if(x<=n)q.push(x);
	else for(size_t i=0;i<flower[x].size();i++)q_push(flower[x][i]);
}
inline void set_st(int x,int b){
	st[x]=b;
	if(x>n)for(size_t i=0;i<flower[x].size();++i)
			set_st(flower[x][i],b);
}
inline int get_pr(int b,int xr){
	int pr=find(flower[b].begin(),flower[b].end(),xr)-flower[b].begin();
	if(pr%2==1){//檢查他在前一層圖是奇點還是偶點
		reverse(flower[b].begin()+1,flower[b].end());
		return (int)flower[b].size()-pr;
	}else return pr;
}
inline void set_match(int u,int v){
	match[u]=g[u][v].v;
	if(u>n){
		edge e=g[u][v];
		int xr=flower_from[u][e.u],pr=get_pr(u,xr);
		for(int i=0;i<pr;++i)set_match(flower[u][i],flower[u][i^1]);
		set_match(xr,v);
		rotate(flower[u].begin(),flower[u].begin()+pr,flower[u].end());
	}
}
inline void augment(int u,int v){
	for(;;){
		int xnv=st[match[u]];
		set_match(u,v);
		if(!xnv)return;
		set_match(xnv,st[pa[xnv]]);
		u=st[pa[xnv]],v=xnv;
	}
}
inline int get_lca(int u,int v){
	static int t=0;
	for(++t;u||v;swap(u,v)){
		if(u==0)continue;
		if(vis[u]==t)return u;
		vis[u]=t;//這種方法可以不用清空v陣列 
		u=st[match[u]];
		if(u)u=st[pa[u]];
	}
	return 0;
}
inline void add_blossom(int u,int lca,int v){
	int b=n+1;
	while(b<=n_x&&st[b])++b;
	if(b>n_x)++n_x;
	lab[b]=0,S[b]=0;
	match[b]=match[lca];
	flower[b].clear();
	flower[b].push_back(lca);
	for(int x=u,y;x!=lca;x=st[pa[y]])
		flower[b].push_back(x),flower[b].push_back(y=st[match[x]]),q_push(y);
	reverse(flower[b].begin()+1,flower[b].end());
	for(int x=v,y;x!=lca;x=st[pa[y]])
		flower[b].push_back(x),flower[b].push_back(y=st[match[x]]),q_push(y);
	set_st(b,b);
	for(int x=1;x<=n_x;++x)g[b][x].w=g[x][b].w=0;
	for(int x=1;x<=n;++x)flower_from[b][x]=0;
	for(size_t i=0;i<flower[b].size();++i){
		int xs=flower[b][i];
		for(int x=1;x<=n_x;++x)
			if(g[b][x].w==0||e_delta(g[xs][x])<e_delta(g[b][x]))
				g[b][x]=g[xs][x],g[x][b]=g[x][xs];
		for(int x=1;x<=n;++x)
			if(flower_from[xs][x])flower_from[b][x]=xs;
	}
	set_slack(b);
}
inline void expand_blossom(int b){ // S[b] == 1
	for(size_t i=0;i<flower[b].size();++i)
		set_st(flower[b][i],flower[b][i]);
	int xr=flower_from[b][g[b][pa[b]].u],pr=get_pr(b,xr);
	for(int i=0;i<pr;i+=2){
		int xs=flower[b][i],xns=flower[b][i+1];
		pa[xs]=g[xns][xs].u;
		S[xs]=1,S[xns]=0;
		slack[xs]=0,set_slack(xns);
		q_push(xns);
	}
	S[xr]=1,pa[xr]=pa[b];
	for(size_t i=pr+1;i<flower[b].size();++i){
		int xs=flower[b][i];
		S[xs]=-1,set_slack(xs);
	}
	st[b]=0;
}
inline bool on_found_edge(const edge &e){
	int u=st[e.u],v=st[e.v];
	if(S[v]==-1){
		pa[v]=e.u,S[v]=1;
		int nu=st[match[v]];
		slack[v]=slack[nu]=0;
		S[nu]=0,q_push(nu);
	}else if(S[v]==0){
		int lca=get_lca(u,v);
		if(!lca)return augment(u,v),augment(v,u),true;
		else add_blossom(u,lca,v);
	}
	return false;
}
inline bool matching(){
	memset(S+1,-1,sizeof(int)*n_x);
	memset(slack+1,0,sizeof(int)*n_x);
	q=queue<int>();
	for(int x=1;x<=n_x;++x)
		if(st[x]==x&&!match[x])pa[x]=0,S[x]=0,q_push(x);
	if(q.empty())return false;
	for(;;){
		while(q.size()){
			int u=q.front();q.pop();
			if(S[st[u]]==1)continue;
			for(int v=1;v<=n;++v)
				if(g[u][v].w>0&&st[u]!=st[v]){
					if(e_delta(g[u][v])==0){
						if(on_found_edge(g[u][v]))return true;
					}else update_slack(u,st[v]);
				}
		}
		int d=INF;
		for(int b=n+1;b<=n_x;++b)
			if(st[b]==b&&S[b]==1)d=min(d,lab[b]/2);
		for(int x=1;x<=n_x;++x)
			if(st[x]==x&&slack[x]){
				if(S[x]==-1)d=min(d,e_delta(g[slack[x]][x]));
				else if(S[x]==0)d=min(d,e_delta(g[slack[x]][x])/2);
			}
		for(int u=1;u<=n;++u){
			if(S[st[u]]==0){
				if(lab[u]<=d)return 0;
				lab[u]-=d;
			}else if(S[st[u]]==1)lab[u]+=d;
		}
		for(int b=n+1;b<=n_x;++b)
			if(st[b]==b){
				if(S[st[b]]==0)lab[b]+=d*2;
				else if(S[st[b]]==1)lab[b]-=d*2;
			}
		q=queue<int>();
		for(int x=1;x<=n_x;++x)
			if(st[x]==x&&slack[x]&&st[slack[x]]!=x&&e_delta(g[slack[x]][x])==0)
				if(on_found_edge(g[slack[x]][x]))return true;
		for(int b=n+1;b<=n_x;++b)
			if(st[b]==b&&S[b]==1&&lab[b]==0)expand_blossom(b);
	}
	return false;
}
inline pair<long long,int> weight_blossom(){
	memset(match+1,0,sizeof(int)*n);
	n_x=n;
	int n_matches=0;
	long long tot_weight=0;
	for(int u=0;u<=n;++u)st[u]=u,flower[u].clear();
	int w_max=0;
	for(int u=1;u<=n;++u)
		for(int v=1;v<=n;++v){
			flower_from[u][v]=(u==v?u:0);
			w_max=max(w_max,g[u][v].w);
		}
	for(int u=1;u<=n;++u)lab[u]=w_max;
	while(matching())++n_matches;
	for(int u=1;u<=n;++u)
		if(match[u]&&match[u]<u)
			tot_weight+=g[u][match[u]].w;
	return make_pair(tot_weight,n_matches);
}
inline void init_weight_graph(){
	for(int u=1;u<=n;++u)
		for(int v=1;v<=n;++v)
			g[u][v]=edge(u,v,0);
}
int main(){
	int m;
	scanf("%d%d",&n,&m);
	init_weight_graph();
	for(int i=0;i<m;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g[u][v].w=g[v][u].w=w;
	}
	printf("%lld\n",weight_blossom().first);
	for(int u=1;u<=n;++u)printf("%d ",match[u]);puts("");
	return 0;
}

图的匹配定义/性质/方法(转载

二分图:整个图能被划分为两个点集(X,Y)且在同一点集内的所有点互不相交的图就是二分图。
匹配:在二分子图的边集M中如果M中的每条边的两个端点只有该条边与这两个端点相连,则M称为一个匹配。
匹配边:我们把两个相匹配的点之间的连线称为匹配边。
最大匹配:图中包含边数最多的匹配称为图的最大匹配。
完备匹配:如果有一边的点全都是匹配点,则称这个匹配为完备匹配。
完美匹配:如果所有点都在匹配边上,称这个最大匹配是完美匹配。
最小覆盖: 用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。(也就是说每个边至少有一个端点是匹配点)
路径:就是连着的几条边(1->2->3就是一条路径)
最小路径覆盖问题:在有向无环图中,用最少的路径条数(不相交的路径,即不仅不能有重合的边,连重合的点都没有)覆盖掉所有顶点。
最大独立集问题: 在N个点的图G中选出m个点,使这m个点两两之间没有边.求m最大值.(如果图G满足二分图条件)可以用二分图匹配来做.
关于二分图带权匹配&最大匹配&完备匹配&完美匹配的区分:
带权匹配:使得该二分图的权值和最大(或最小)的匹配。
最大匹配:使得该二分图边数最多的匹配。
完备匹配:使得点数较小的点集中每个点都被匹配的匹配。
完美匹配:所有点都被匹配的匹配。
可知:完美匹配一定是最大匹配和完备匹配。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大匹配数 = 最大独立数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

无权匹配:匈牙利算法
带权匹配:km算法
求二分图的最小匹配:把权值取反,变为负的,再用KM算出最大权匹配,取反则为其最小权匹配

猜你喜欢

转载自blog.csdn.net/Nrtostp/article/details/81290955