1. 程式人生 > >POJ 2186 Popular Cows Tarjan+縮點

POJ 2186 Popular Cows Tarjan+縮點

題意:給定一個有向圖,求有多少個頂點是任意頂點出發都可達的。

定理:有向無環圖中唯一出度為0的點,一定可以由任何點出發均可達(由於無環所以從任何點出發往前走,必然終止於一個出度為0的點。)

思路:

1.求出所有的強連通分量(用tarjan()演算法)

2.每個強連通分量縮成一點,則形成一個有向無環圖DAG。

3.DAG上面如果有唯一的出度為0的點,則該點能被所有的點可達。那麼該點所代表的連通分量上的所有的原圖中的點,都能被原圖中的所有點可達,則該連通分量的點數,就是答案。

4.DAG 上面如果有不止一個出度為0的點,則這些點互相不可達,原問題無解,答案為0。

縮點的時候不一定要構造新圖,只要把不同強連通分量的點染成不同顏色,然後考察各種顏色的點有沒有連到別的顏色的邊即可(即其對應的縮點後的DAG圖上的點是否有出邊)。

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=10010;
vector<int>g[maxn];
int n,m,x,y;
int v,c[maxn],l=0,low[maxn],dfn[maxn],f[maxn];
int cnt=0,out0[maxn],sum[maxn],time_clock=0;
void tarjan(int u)
{
	low[u]=dfn[u]=++time_clock;
	c[++l]=u;
	for(int i=0;i<g[u].size();i++)
	{
		v=g[u][i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(!f[v])
		{
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u])
	{
		int len=l;
		cnt++;
		while(c[l]!=u)
		{
			f[c[l--]]=cnt;
		}
		f[c[l--]]=cnt;
		sum[cnt]=len-l;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
	}
	memset(dfn,0,sizeof(dfn));
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])
           tarjan(i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<g[i].size();j++)
		{
			v=g[i][j];
			if(f[i]!=f[v])
			 out0[f[i]]++;
		}
	}
	x=0;
	for(int i=1;i<=cnt;i++)
	{
		if(!out0[i])
		{
	       if(x>0)
		  {
		     printf("0");
		     return 0;	
		  }	
		   x=sum[i];
		}    
	} 
	printf("%d",x);
	return 0;
}