poj1904King's Quest(强连通)
/*poj1904King's Quest
题意是,N个男生和N个女生,告诉你每个男生喜欢的女生编号,然后给出一个初始匹配(这个初始匹配是完备匹配),然后求所有可能的完备匹配,按升序输出。
看了一个神牛的报告,把这个转化成强连通问题:
首先按照给出的有向边建图,然后根据最后的那个完备匹配在图中加入反向边(就是根据那个完备匹配连 女生 到 男生 的边),那么在这个图中,属于同一个强连通的点对一定是合法点对。把他们排序输出即可。*/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2002
#define INF 100000000
int u[MAXN*MAXN],v[MAXN*MAXN],head[MAXN*MAXN],next[MAXN*MAXN],ans[MAXN];
int DFN[MAXN*2],LOW[MAXN*2],instack[MAXN*2],Stap[MAXN*2],ID[MAXN*2];
int Dindex,Stop,Bcnt;
void tarjan(int i)//缩点 强联通
{
int j;
DFN[i]=LOW[i]=++Dindex;
instack[i]=true;
Stap[++Stop]=i;
for(int e=head[i];e>=0;e=next[e])
{
j=v[e];
if(!DFN[j])
{
tarjan(j);
if(LOW[i]>LOW[j])
LOW[i]=LOW[j];
}
else if(instack[j]&&DFN[j]<LOW[i])
LOW[i]=DFN[j];
}
if(DFN[i]==LOW[i])
{
Bcnt++;
do
{
j=Stap[Stop--];
instack[j]=false;
ID[j]=Bcnt;
}while(j!=i);
}
}
int n;
void pro()
{
Dindex=Bcnt=Stop=0;
memset(DFN,0,sizeof(DFN));
memset(ID,-1,sizeof(ID));
for(int i=1;i<=2*n;i++)
{
if(!DFN[i])tarjan(i);
}
}
int main()
{
int c,d,num;
while(scanf("%d",&n)==1)
{
num=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)//建立图
{
scanf("%d",&c);
for(int j=0;j<c;j++)
{
scanf("%d",&d);
u[num]=i;
v[num]=n+d;
next[num]=head[i];
head[i]=num;
num++;
}
}
for(int i=1;i<=n;i++)//根据完备匹配 反向建立边
{
scanf("%d",&d);
u[num]=d+n;
v[num]=i;
next[num]=head[d+n];
head[d+n]=num;
num++;
}
pro();
for(int i=1;i<=n;i++)//按男生顺序判断
{
num=0;
for(int e=head[i];e>=0;e=next[e])
{
int s=ID[u[e]];
int t=ID[v[e]];
if(s==t)//在一个连通域中的点 是一对合法的点
{
ans[num++]=v[e];
}
}
sort(ans,ans+num);//升序
printf("%d",num);
for(int i=0;i<num;i++)
printf(" %d",ans[i]-n);
printf("\n");
}
}
}