您的位置:首页 > 产品设计 > UI/UE

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

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

题意:有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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: