蛮好的一道题,初看觉得是二分图匹配,但是仔细思索却是强连通分量。关于题目解释可以看这里点击打开链接
题意:有n个王子,有n个美女,每个王子可能同时喜欢多个美女,数据已经给出一组完全匹配的方案。问在满足所有王子都能完全匹配的情况下,每个王子能选择的对象分别有谁,按升序输出。(其实如标题的描述更简洁。。)
一开始可能会想,对于每个王子,先拆边,再重新挑选对象,判断可行性。注意到题目数据量很大,最多2000个节点,200000条边,如果直接暴力拆边判可行,复杂度就是O(N*N*M)……注意到题目已经给出了一组合法的方案,肯定是有用的。 假设 A 王子和原配 B 美女解除关系,再匹配了 C 美女,那么 C 美女的原配 D 王子必定要重新再找另一个美女,如果任意一个王子能找回 B 美女匹配,证明 A 王子和 C 美女是可能的匹配对,否则就是不可能的匹配对。
构图:每个王子向喜欢的美女连接一条有向边,再根据匹配好的方案,每个美女向其匹配的王子连接一条有向边。。。构图后简化一下描述就是:如果从 A 点出发,最终能回到 A 点(成环)则能够维持完全匹配的,因此求一次强连通分量,判断每个王子与其喜欢的美女是否在同一强连通分量即可。
这里再补充一点,对于这个图中的每一个强连通分量,里面的男和女的数目都是一样的。这是因为男的只连去女的,女的只连去男的,且女的连去男的只有一根连线。
/******************************************************************************* # Author : Neo Fung # Email : neosfung@gmail.com # Last modified: 2012-07-24 19:23 # Filename: ZOJ2470 POJ1904 King's Quest.cpp # Description : ******************************************************************************/ #ifdef _MSC_VER #define DEBUG #define _CRT_SECURE_NO_DEPRECATE #endif #include <fstream> #include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <limits.h> #include <algorithm> #include <math.h> #include <numeric> #include <functional> #include <ctype.h> #include <vector> using namespace std; const int kMAX=10010; const double kEPS=10E-6; int STACK[kMAX],top=0; //Tarjan 算法中的栈 bool InStack[kMAX]; //检查是否在栈中 int DFN[kMAX]; //深度优先搜索访问次序 int Low[kMAX]; //能追溯到的在栈中的最早次序 int ComponentNumber=0; //有向图强连通分量个数 int Index=0; //索引号 vector <int> Edge[kMAX]; //邻接表表示 int InComponent[kMAX]; //记录每个点在第几号强连通分量里 int ComponentDegree[kMAX]; //记录每个强连通分量的度 void Tarjan(int i) { int j; DFN[i]=Low[i]=Index++; InStack[i]=true; STACK[++top]=i; for (size_t e=0;e<Edge[i].size();e++) { j=Edge[i][e]; if (DFN[j]==-1) { Tarjan(j); Low[i]=min(Low[i],Low[j]); } else if (InStack[j]) //如果指向的节点j仍在栈中,由于j先于i入栈,则j有到i的通路,同时由于i指向j,则i与j构成回路 Low[i]=min(Low[i],DFN[j]); //如果指向的节点扔在栈中,则指向的节点仍未编入强连通分量 //如果前面两个判断条件都是错误的话,则i和j不在同一个连通分量中 } if (DFN[i]==Low[i]) //连通分量中最早进栈的点 { ComponentNumber++; do { j=STACK[top--]; InStack[j]=false; InComponent[j]=ComponentNumber; //给每一个连通分量上的节点染色 } while (j!=i); } } int output[kMAX]; void solve(int N) //N是此图中点的个数,注意是0-indexed! { memset(STACK,-1,sizeof(STACK)); memset(InStack,0,sizeof(InStack)); memset(DFN,-1,sizeof(DFN)); memset(Low,-1,sizeof(Low)); for(int i=1;i<=N;i++) if(DFN[i]==-1) Tarjan(i); int n=N/2; for(int i=1;i<=n;++i) { int x=InComponent[i]; size_t cnt=0; for(size_t j=0;j<Edge[i].size();++j) if(InComponent[Edge[i][j]]==x) output[cnt++]=Edge[i][j]-n; sort(output,output+cnt); printf("%d",cnt); for(size_t j=0;j<cnt;++j) printf(" %d",output[j]); printf("\n"); } // printf("\n"); } int main(void) { #ifdef DEBUG freopen("../stdin.txt","r",stdin); freopen("../stdout.txt","w",stdout); #endif int n,tmp,u,v; while(~scanf("%d",&n) && n) { for(int i=0;i<=2*n;++i) Edge[i].clear(); for(int i=1;i<=n;++i) { scanf("%d",&tmp); while(tmp--) { scanf("%d",&v); Edge[i].push_back(v+n); } } for(int i=1;i<=n;++i) { scanf("%d",&u); Edge[u+n].push_back(i); } solve(n+n); } return 0; }