ZOJ2470 POJ1904 King's Quest,强连通分量

来源:互联网 发布:mysql host 编辑:程序博客网 时间:2024/05/22 12:26

蛮好的一道题,初看觉得是二分图匹配,但是仔细思索却是强连通分量。关于题目解释可以看这里点击打开链接

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


原创粉丝点击