您的位置:首页 > 其它

poj 1904 强连通分量 tarjan

2013-08-27 22:37 316 查看
King's Quest

Time Limit: 15000MSMemory Limit: 65536K
Total Submissions: 6569Accepted: 2342
Case Time Limit: 2000MS
Description

Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which of those girls he did like. The sons of the king were young and light-headed, so it was possible for one son to like several girls.

So the king asked his wizard to find for each of his sons the girl
he liked, so that he could marry her. And the king's wizard did it --
for each son the girl that he could marry was chosen, so that he liked
this girl and, of course, each beautiful girl had to marry only one of
the king's sons.

However, the king looked at the list and said: "I like the list you
have made, but I am not completely satisfied. For each son I would like
to know all the girls that he can marry. Of course, after he marries any
of those girls, for each other son you must still be able to choose the
girl he likes to marry."

The problem the king wanted the wizard to solve had become too hard
for him. You must save wizard's head by solving this problem.

Input

The
first line of the input contains N -- the number of king's sons (1 <=
N <= 2000). Next N lines for each of king's sons contain the list of
the girls he likes: first Ki -- the number of those girls, and then Ki
different integer numbers, ranging from 1 to N denoting the girls. The
sum of all Ki does not exceed 200000.

The last line of the case contains the original list the wizard had
made -- N different integer numbers: for each son the number of the girl
he would marry in compliance with this list. It is guaranteed that the
list is correct, that is, each son likes the girl he must marry
according to this list.

Output

Output N
lines.For each king's son first print Li -- the number of different
girls he likes and can marry so that after his marriage it is possible
to marry each of the other king's sons. After that print Li different
integer numbers denoting those girls, in ascending order.
Sample Input

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4

Sample Output

2 1 2
2 1 2
1 3
1 4

Hint

This problem has huge input and output data,use scanf() and printf() instead of cin and cout to read data to avoid time limit exceed.
Source

Northeastern Europe 2003

推导来自这里:http://www.cnblogs.com/ZShogg/archive/2013/03/24/2978513.html

分析:这个题的建图方法很特别。为了充分利用条件,即最后给出的那个完美匹配,将每个王子向他的梦中情人作一条有向边,完美匹配方案中,美女向匹配的王子作一条有向边。求出图中的强连通分量,与王子在同一个强连通分量且该王子喜欢的美女就是答案。

正确性:王子集合{x1,x2,......,xn},美女集合{y1,y2,......,yn},假设在原完美匹配方案中每个匹配都是 (xi,yi),显然yi是xi的一个选项。假如xi选了yj(j!=i),则原先与yj匹配的王子xj要找另一个女人,yi要与另一个王子匹配,假如 xj喜欢yi,那么yj就可以是xi的另一个选项了,假如xj不喜欢yi,那么就继续下去拆散现有的完美匹配,直到有个王子喜欢yi。所以这样就相当于要 找一条由xi出发到yi的通路(等价于由xi出发回到xi的回路),只要这样的回路存在,王子就可以与回路中任意一个他喜欢的美女匹配了。这样也就相当于 求包含xi的强连通分量。(注意:求出强连通分量之后,只有王子喜欢的美女才能匹配)。

算法和他想的差不多,其中tarjan犯错的地方为:1.当找到一个强连通分量时,不能记录其上所有boy的喜好;2.再敲tarjan之前还纠结过一个点属于多个强连通分量的情况。。。其实这多个就是一个,AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<algorithm>

using namespace std;

#define LL long long
#define ULL unsigned long long
#define UINT unsigned int
#define MAX_INT 0x7fffffff
#define MAX_LL 0x7fffffffffffffff
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))

#define MAXN 4444
#define MAXM 444444

struct edge{
int u,v,nxt;
}e[MAXM];
int cc,h[MAXN],ins[MAXN];
int dfn[MAXN],low[MAXN],vis[MAXN];
int m[2010][2010];
int tsp,sta[MAXN],top,sn;
int N,M;

vector<int> g[MAXN];
int c[MAXN];

inline void add(int u, int v){
e[cc]=(edge){u, v, h[u]};
h[u]=cc++;
}

void tarjan(int s){
low[s]=dfn[s]=++tsp;
vis[s]=ins[s]=1;
sta[top++]=s;

for(int i=h[s]; i!=-1; i=e[i].nxt){
int v=e[i].v;
if(!vis[v]) tarjan(v),low[s]=MIN(low[s], low[v]);
else if(ins[v]) low[s]=MIN(low[s], dfn[v]);
}

if(low[s]==dfn[s]){                 //strongly connnected circle
int v;
do{
v=sta[--top];
if(v>N){                    //girl
g[sn].push_back(v);
}
else c[v]=sn;               //boy, hash
ins[v]=0;
}while(v!=s);
sort(g[sn].begin(), g[sn].end());
sn++;
}
}

int ans[MAXN];
void solve(int n){
memset(ins, 0, sizeof(ins));
memset(vis, 0, sizeof(vis));
tsp=top=0;
sn=0;
for(int i=1; i<=n; i++)
if(!vis[i]) tarjan(i);

for(int i=1; i<=n; i++){
vector<int> ans;
int j=c[i];
//cout<<g[j].size()<<endl;
for(int q=0; q<g[j].size(); q++) if(m[i][g[j][q]-n])        //选择相邻点
ans.push_back(g[j][q]-n);                               //reverse hash
int l=ans.size();
printf("%d ",l);
for(int k=0; k<l; k++) printf("%d%c",ans[k], (k==l-1 ? '\n' : ' '));
}
}

int main(){
while(scanf(" %d",&N)==1){
int i,j,k;
memset(h, -1, sizeof(h));       cc=0;
memset(m, 0, sizeof(m));
for(i=0; i<N; i++){
scanf(" %d",&j);
for(k=0; k<j; k++){
int t;
scanf(" %d",&t);
add(i+1, N+t);          //i+1 喜欢N+t
m[i+1][t]=1;
}
}
for(i=0; i<N; i++){
scanf(" %d",&j);
add(j+N, i+1);              //i+1喜欢j+N
}
M=2*N;
solve(N);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: