[WC2016]挑戰NPC (一般圖最大匹配----帶花樹)

 

 

 

題解:

首先所有的球都得裝進桶裏,所以每個球都向可行的桶連邊

一個桶最多可以裝3個球,考慮把一個桶拆成三個點

現在要求半空桶的最大數量,如果一個桶只有一個點匹配,那麼就cnt++

但是這樣可能會有兩個原本可以分開放的點匹配到同一個桶導致答案減小

有一種巧妙的建圖方法,可以使能分開放的點儘量分開

把一個桶拆成的三個點兩兩連邊

這樣,在求最大匹配的情況下放在一起連的最大匹配數爲3,而分開的最大匹配數爲4

就可以解決最大化半空桶數的問題

如圖:(紅色爲匹配邊)

但是由於這樣建出來的圖並不是二分圖,而是一般圖

所以我們需要解決一般圖的最大匹配問題

帶花樹

一個一般圖如果帶有奇環,那麼它就一定不是一個二分圖

假設圖中存在這樣一個奇環,它可能會貢獻五條匹配邊

但是它的內部最多隻會貢獻2條匹配邊

此時最多隻有一個外面的點能匹配奇環中的點

於是我們可以考慮把奇環縮成一個點來找增廣路徑

但是縮完點之後,本來可以匹配5條邊的圖就只存在了1+2條匹配

所有我們不能直接縮點,在dfs到該點的時候還需要對縮掉的點進行展開(開花)

這樣再來求增廣路徑

下面一段引用自:https://www.cnblogs.com/owenyu/p/6858508.html

貼個模板:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 305
#define M 500005
int fir[N],to[M],nxt[M],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
namespace FT{
	int sz;
	int fa[N],pre[N],mat[N],vis[N];
	int q[N],he,ta;
	int tim[N],tm;
	int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	int lca(int x,int y){
		for(tm++;;swap(x,y))if(x){
			x=find(x);
			if(tim[x]==tm)return x;
			tim[x]=tm;x=pre[mat[x]];
		}
	}
	void bls(int x,int y,int p){
		while(find(x)!=p){
			pre[x]=y;y=mat[x];
			if(vis[y]==2)vis[y]=1,q[++ta]=y;
			if(find(x)==x)fa[x]=p;
			if(find(y)==y)fa[y]=p;
			x=pre[y];
		}
	}
	bool bfs(int s){
		for(int i=1;i<=sz;i++)fa[i]=i;
		memset(vis,0,sizeof(vis));memset(pre,0,sizeof(pre));
		he=1;ta=0;q[++ta]=s;vis[s]=1;
		while(he<=ta){
			int u=q[he++];
			for(int v,p=fir[u];p;p=nxt[p]){
				if(vis[v=to[p]]==2||find(u)==find(v))continue;
				if(!vis[v]){
					vis[v]=2;pre[v]=u;
					if(!mat[v]){
						for(int tmp;v;v=tmp){
							tmp=mat[u=pre[v]];
							mat[v]=u;mat[u]=v;
						}
						return 1;
					}
					vis[mat[v]]=1;q[++ta]=mat[v];
				}
				else{
					int x=lca(u,v);
					bls(u,v,x);bls(v,u,x);
				}
			}
		}
		return 0;
	}
}//----FT----

 

本題代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 605
#define M 500005
int fir[N],to[M],nxt[M],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int fa[N],pre[N],match[N],vis[N],q[N],he,ta;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int sz,tim[N],tm;
int lca(int x,int y)
{
	for(tm++;;swap(x,y))if(x){
		x=find(x);
		if(tim[x]==tm)return x;
		else tim[x]=tm,x=pre[match[x]];
	}
}
void bls(int x,int y,int p)
{
	while(find(x)!=p){
		pre[x]=y;y=match[x];
		if(vis[y]==2)vis[y]=1,q[++ta]=y;
		if(find(x)==x)fa[x]=p;
		if(find(y)==y)fa[y]=p;
		x=pre[y];
	}
}
bool bfs(int s)
{
	for(int i=1;i<=sz;i++)fa[i]=i;
	memset(vis,0,sizeof(vis));memset(pre,0,sizeof(pre));
	he=1;ta=0;q[++ta]=s;vis[s]=1;
	while(he<=ta){
		int u=q[he];he++;
		for(int v,p=fir[u];p;p=nxt[p]){
			if(vis[v=to[p]]==2||find(v)==find(u))continue;
			if(!vis[v]){
				vis[v]=2;pre[v]=u;
				if(!match[v]){
					for(int tmp;v;v=tmp){
						tmp=match[u=pre[v]];
						match[v]=u,match[u]=v;
					}
					return 1;
				}
				vis[match[v]]=1;q[++ta]=match[v];
			}
			else{
				int x=lca(u,v);
				bls(u,v,x);
				bls(v,u,x);
			}
		}
	}
	return 0;
}
int main()
{
	int T,n,m,E,i,u,v;
	scanf("%d",&T);
	while(T--){
		memset(fir,0,sizeof(fir));cnt=0;
		memset(match,0,sizeof(match));
		scanf("%d%d%d",&n,&m,&E);
		sz=n+3*m;
		for(i=1;i<=E;i++){
			scanf("%d%d",&u,&v);
			adde(u,n+v);
			adde(u,n+m+v);
			adde(u,n+2*m+v);
		}
		for(i=1;i<=m;i++){
			adde(n+i,n+m+i);
			adde(n+m+i,n+2*m+i);
			adde(n+i,n+2*m+i);
		}
		int ans=0;
		for(i=1;i<=sz;i++)
			if(!match[i])ans+=bfs(i);
		printf("%d\n",ans-n);
		printf("%d",(match[1]-n-1)%m+1);
		for(i=2;i<=n;i++)
			printf(" %d",(match[i]-n-1)%m+1);
		printf("\n");
	}
}

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章