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

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;

}

你可能感兴趣的:(二分图)