题意:有N男N女,告诉其中一个完备匹配,求其它所有的完备匹配
分析:对于一个男生,如果还有别的可能,那么肯定可以找到一个环。但这图怎么构造就悲剧了,一直建了个错图,所以一直WA。后面还是看了别人的解题报告才。。。下面分别给出WA和AC的思想与代码
//WA
//男生喜欢女生的建双向边,然后算边连通分量,如果已匹配的边的两顶点不在同一边连通分量中
//那么只有这组关系,然后不断删边,确定关系,删边。。。
/*
下面的样例相当于两个环只有一个公共顶点相连
4
3 1 2 3
3 1 2 3
2 3 4
2 3 4
1 2 3 4
*/
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=2200;
vector<int>e[maxn],e1[maxn];
bool flag[maxn],inq[maxn];
int n,top,num,tnum,mat[maxn],q[maxn],DFN[maxn],low[maxn],cnt[maxn];
void Tarjan(int t,int pt)
{
DFN[t]=low[t]=++tnum;
inq[t]=true;
q[top++]=t;
int i,j;
for(i=0;i<e[t].size();i++)
{
j=e[t][i];
if(j==pt) continue;
if(!DFN[j])
{
Tarjan(j,t);
if(low[t]>low[j])
low[t]=low[j];
}
else if(inq[j]&&low[t]>DFN[j])
low[t]=DFN[j];
}
if(low[t]==DFN[t])
{
++num;
do
{
inq[q[--top]]=false;
cnt[q[top]]=num;
}while(q[top]!=t);
}
}
int main()
{
int i,j,k,ii,jj,kk,head,tail;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=2*n;i++)
{
e1[i].clear();
e[i].clear();
}
for(i=1;i<=n;i++)
{
scanf("%d",&k);
for(j=0;j<k;j++)
{
scanf("%d",&ii);
e[i].push_back(ii+n);
e[ii+n].push_back(i);
}
}
for(i=1;i<=n;i++)
{
scanf("%d",&j);
e1[i].push_back(j+n);
mat[i]=j+n;
mat[j+n]=i;
}
memset(DFN,0,sizeof(DFN));
memset(low,0,sizeof(low));
memset(cnt,0,sizeof(cnt));
memset(inq,false,sizeof(inq));
for(top=0,tnum=0,num=0,i=1;i<=2*n;i++)
if(!DFN[i])
Tarjan(i,-1);
memset(flag,false,sizeof(flag));
for(head=tail=0,i=1;i<=n;i++)
if(!flag[i]&&!flag[mat[i]]&&cnt[i]!=cnt[mat[i]])
{
q[tail++]=i;
q[tail++]=mat[i];
}
while(head!=tail)
{
i=q[head++];
flag[i]=true;
if(e[i].size()==0) continue;
j=e[i][0];
for(k=0;k<e[j].size();k++)
{
jj=e[j][k];
for(ii=0;ii<e[jj].size();ii++)
if(e[jj][ii]==j)
{
swap(e[jj][e[jj].size()-1],e[jj][ii]);
e[jj].pop_back();
break;
}
if(e[jj].size()<=1)
q[tail++]=jj;
}
}
for(i=1;i<=n;i++)
{
if(flag[i]) continue;
for(k=0;k<e[i].size();k++)
if(!flag[e[i][k]]&&e[i][k]!=e1[i][0]&&cnt[i]==cnt[e[i][k]])
e1[i].push_back(e[i][k]);
}
for(i=1;i<=n;i++)
{
sort(e1[i].begin(),e1[i].end());
printf("%d",e1[i].size());
for(j=0;j<e1[i].size();j++)
printf(" %d",e1[i][j]-n);
printf("\n");
}
}
return 0;
}
AC的代码如下:
//男生喜欢女生的建单向边,对于已经搭配好的,连女的至男的。
//然后求强连通分量,对于每个男生,如果和所连接的女生在同一个强连通分量中
//那么就可以跟其在一起
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=4200;
vector<int>e[maxn],e1[maxn];
bool flag[maxn],inq[maxn];
int n,top,num,tnum,mat[maxn],q[maxn],DFN[maxn],low[maxn],cnt[maxn];
void Tarjan(int t,int pt)
{
DFN[t]=low[t]=++tnum;
inq[t]=true;
q[top++]=t;
int i,j;
for(i=0;i<e[t].size();i++)
{
j=e[t][i];
if(!DFN[j])
{
Tarjan(j,t);
if(low[t]>low[j])
low[t]=low[j];
}
else if(inq[j]&&low[t]>DFN[j])
low[t]=DFN[j];
}
if(low[t]==DFN[t])
{
++num;
do
{
inq[q[--top]]=false;
cnt[q[top]]=num;
}while(q[top]!=t);
}
}
int main()
{
int i,j,k,ii;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=2*n;i++)
{
e1[i].clear();
e[i].clear();
}
for(i=1;i<=n;i++)
{
scanf("%d",&k);
for(j=0;j<k;j++)
{
scanf("%d",&ii);
e[i].push_back(ii+n);
}
}
for(i=1;i<=n;i++)
{
scanf("%d",&j);
e[j+n].push_back(i);
}
memset(DFN,0,sizeof(DFN));
memset(low,0,sizeof(low));
memset(cnt,0,sizeof(cnt));
memset(inq,false,sizeof(inq));
for(top=0,tnum=0,num=0,i=1;i<=2*n;i++)
if(!DFN[i])
Tarjan(i,-1);
for(i=1;i<=n;i++)
{
for(k=0;k<e[i].size();k++)
if(cnt[i]==cnt[e[i][k]])
e1[i].push_back(e[i][k]);
}
for(i=1;i<=n;i++)
{
sort(e1[i].begin(),e1[i].end());
printf("%d",e1[i].size());
for(j=0;j<e1[i].size();j++)
printf(" %d",e1[i][j]-n);
printf("\n");
}
}
return 0;
}