UVALive-2966 King's Quest(强连通+二分图匹配)
题目大意:有n个男孩和和n个女孩,已只每个男孩喜欢的女孩。一个男孩只能娶一个女孩、一个女孩只能嫁一个男孩并且男孩只娶自己喜欢的女孩,现在已知一种他们的结婚方案,现在要求找出每个男孩可以娶的女孩(娶完之后不能影响其他男孩结婚)。
题目分析:已知的结婚方案是一个完全匹配。从每个男孩出发向他喜欢的女孩连一条有向边,得到一张完全二分图,实际上这道题是让判断去掉哪一些边使图仍然完全匹配。设男生x1和女生y1是已知方案中要结婚的两个人,假如x1抛弃y1,选择了他也喜欢的y2结婚(也就是去掉边x1->y2),那么就得需要让原方案中y2的结婚对象x2选择一个他喜欢的女孩(不能再是y2)结婚,一直进行下去这个过程,y1终究会被选走(如果去边之后的图仍完全匹配)。这正是匈牙利算法的过程,但这样要超时。但是,如果将从y1出发连一条边到x1,那么这个过程所经过的所有点就构成了一个强连通分量。对于某个男孩,娶和他在同一强连通分量的任何一个女孩都不会影响其他男孩结婚。
代码如下:
# include<iostream> # include<cstdio> # include<stack> # include<vector> # include<cstring> # include<iostream> using namespace std; # define REP(i,s,n) for(int i=s;i<n;++i) # define CL(a,b) memset(a,b,sizeof(a)) # define CLL(a,b,n) fill(a,a+n,b) const int N=2005; struct Edge { int to,nxt; }; Edge e[N*100+N]; stack<int>S; int scc_cnt,dfs_cnt,cnt,n,head[2*N],sccno[2*N]; int low[2*N],pre[2*N],G[N][N]; vector<int>ans; void add(int u,int v) { e[cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt++; } void dfs(int u) { S.push(u); low[u]=pre[u]=++dfs_cnt; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(!pre[v]){ dfs(v); low[u]=min(low[u],low[v]); }else if(!sccno[v]) low[u]=min(low[u],pre[v]); } if(low[u]==pre[u]){ ++scc_cnt; while(1){ int x=S.top(); S.pop(); sccno[x]=scc_cnt; if(x==u) break; } } } void findScc() { scc_cnt=dfs_cnt=0; CL(pre,0); CL(sccno,0); REP(i,1,n+1) if(!pre[i]) dfs(i); } int main() { while(~scanf("%d",&n)) { int k,a; cnt=0; CL(G,0); CL(head,-1); REP(i,1,n+1){ scanf("%d",&k); while(k--) { scanf("%d",&a); add(i,a+n); G[i][a]=1; } } REP(i,1,n+1){ scanf("%d",&a); add(a+n,i); } findScc(); REP(i,1,n+1){ ans.clear(); REP(j,n+1,2*n+1) if(G[i][j-n]&&sccno[i]==sccno[j]) ans.push_back(j-n); printf("%d",ans.size()); REP(j,0,ans.size()) printf(" %d",ans[j]); printf("\n"); } } return 0; }