求出二分图所有可能的匹配边

pku 1904

题目大意是有一个国王,他有n个儿子,现在有n个美丽的女子准备嫁给他的n个儿子。国王已经让他的大臣收集了每个儿子喜欢的姑娘的编号(可以喜欢很多个),并且大臣们已经找出了一个完美匹配的方案。现在国王不满意,他想要知道所有可能的匹配方案(前提是每个儿子可以娶到一个姑娘)。即求出每个儿子所有可能的结婚方案。

这题的解法是:

1、根据题目信息建图(王子->姑娘)。

2、根据题目给定的完美匹配建图(姑娘->王子)。

3、求强连通分量

4、如果某个姑娘和王子在一个连通分量,并且他们之间在原图上存在直接边,那么这个姑娘便是王子的可能匹配

分析:

根据题意,每个王子喜欢的姑娘已经确定了,他只能娶他喜欢的。既然是n个王子,n个姑娘。也就是说,最终最终还是一对一,即每个王子必须要有一个老婆。

假如某一个王子已经和一个姑娘完美匹配了,现在可能存在其他方案,假如这个方案中,王子a和王子b喜欢的某一个姑娘匹配,那么对于王子b来说,他就少了一个老婆的选择了,那怎么办呢?他就只能再去匹配上一个别的王子的姑娘。一次类推,那么这个王子怎么办呢?很有可能最后一个王子便没有老婆了。如果要避免最后一个王子没有老婆的情况发生,那么他就只能管第一个王子要了,谁让他抢走了别人的姑娘呢。这么一折腾,就形成了一个环,也就是一个强连通分量。也就是说,在一个强连通分量里,王子间是可以相互交换老婆的...那么为什么还要原图存在直接边呢?因为直接边说明王子喜欢这个女孩,如果他要了一个自己不喜欢的,那么别的王子自然会少掉一个喜欢的女孩。这是不道德的~

下面是代码:

#include<iostream>
#include<string>
#include<stack>
using namespace std;

typedef struct node
{
	int v;
	struct node *next;
}node;

node *link[5000];
node edge[400000];
int num,n;

void add(int u,int v)
{
	edge[num].v=v;
	edge[num].next=link[u];
	link[u]=edge+num++;
}

int find(int u,int v)
{
	for(node *p=link[u];p;p=p->next)
	{
		if(p->v==v)
			return 1;
	}
	return -1;
}

int dnf[5000],low[5000],instack[5000],belong[5000];
int cut,mm;
stack<int>Q;

void tarjan(int u)
{
	low[u]=dnf[u]=++mm;
	instack[u]=1;
	Q.push(u);
	for(node *p=link[u];p;p=p->next)
	{
		if(!dnf[p->v])
		{
			tarjan(p->v);
			if(low[u]>low[p->v])
			{
				low[u]=low[p->v];
			}
		}
		else if(instack[p->v] && low[u]>dnf[p->v])
		{
			low[u]=dnf[p->v];
		}
	}
	int v;
	if(low[u]==dnf[u])
	{
		cut++;
		do
		{
			v=Q.top();
			Q.pop();
			instack[v]=0;
			belong[v]=cut;
		}while(u!=v);
	}
}

void solve()
{
	cut=0;
	mm=0;
	memset(instack,0,sizeof(instack));
	memset(dnf,0,sizeof(dnf));
	for(int i=1;i<=2*n;i++)
	{
		if(!dnf[i])
			tarjan(i);
	}
}

int main()
{
	int i,j,k,a;
	freopen("D:\\in.txt","r",stdin);
	while(scanf("%d",&n)!=EOF)
	{
		memset(link,0,sizeof(link));
		num=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&k);
			for(j=1;j<=k;j++)
			{
				scanf("%d",&a);
				add(i,a+n);
			}
		}
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a);
			add(a+n,i);
		}
		solve();
		int ans[2001];
		for(i=1;i<=n;i++)
		{
			k=0;
			for(j=n+1;j<=2*n;j++)
			{
				if(belong[i]==belong[j] && find(i,j)!=-1)
				{
					ans[k++]=j-n;	
				}
			}
			printf("%d",k);
			for(j=0;j<k;j++)
			{
				printf(" %d",ans[j]);
			}
			printf("\n");
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ka200812/p/2122013.html