题意:有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; }