POJ2186 Popular Cows(Tarjan算法)

2186

Accepted

1708K

625MS

C++

2318B

用STL写的,哈哈,居然没有超时,很欣慰啊,贴出来大家参考一下……

/*
	后来看了别人的代码后发现,其实可以找到强连通分量之后,
	把其中的每个元素都染成一个颜色(开一个数组paint[]来记录染色情况,颜色用num表示,初始化为),
	最后枚举所有边,如果一条边(u,v)两个端点的颜色不一样,那么就让outdgr[paint[u]]++。
	即让这个强连通分支的出度加一
	当然,这样最后outdgr中存的并不是强连通分量的出度,但如果outdgr的值为,那么该强连通分量的值也一定为,
	因此用outdgr来判断强连通分量的出度是否为是完全可以的。
*/
#include<iostream>
#include<vector>
#include<stack>
using namespace std;

#define Max 10005
bool visit[Max];		//标记该点是否已经完全访问过了,即是否已经发现过了
bool inS[Max];		//标记是否在栈中
vector<int> v[Max];	///类似与邻接表,用于存储图
vector<int> conn[Max];//记录各个强连通分支
int dfn[Max];			//用于记录第一次访问时的时间
int low[Max];			//low及其后代所能访问追溯到的最早祖先的dfn的值
int out[Max];     //记录强连通分支的出度
int paint[Max];   //给强连通分支内的元素染色,如果一个边的两个端点颜色不一样,则强连通分支的出度加一
int time;			//时间戳
int num;			//连通分支数量
int N,M;			//节点与操作数
stack<int> S;

void Tarjan(int u)
{
	int next;
	low[u] = dfn[u] = ++time;
	S.push(u);
	inS[u] = true;
	visit[u] = true;
	for(int i=0;i<v[u].size();i++)
	{
		next = v[u][i];
		if(!visit[next])//如果u的下一个顶点还没有访问
		{
			Tarjan(next);
			low[u] = min(low[u],low[next]);
		}else{
			if(inS[u])//已经访问过该点了,而且还在栈里
				low[u] = min(low[u],dfn[next]);
		}
	}
	if(dfn[u]==low[u])//如果dfn==low证明该点为深搜树的树根
	{
		do{
			next = S.top();
			paint[next] = num;
			conn[num].push_back(next);//依次记录各个连通分支
			S.pop();
			inS[next] = false;
		}while(next!=u);
		num++;//计数
	}
}
int main()
{
	int i;
	int a,b;
	int count;
	while(scanf("%d%d",&N,&M)!=EOF)
	{
		for(i=0;i<=N;i++)
		{
			conn[i].clear();
			v[i].clear();//在下一次运行前清空邻接表
		}
		memset(visit,false,sizeof(visit));
		memset(inS,false,sizeof(inS));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(out,0,sizeof(out));
		memset(paint,-1,sizeof(paint));
		time = 0;
		num = 0;//初始化完毕
		for(i=0;i<M;i++)
		{
			scanf("%d%d",&a,&b);
			v[a].push_back(b);//将b加入a的邻接表中
		}
		for(i=1;i<=N;i++)
			if(!visit[i])
				Tarjan(i);//对元素遍历求出强连通分支和分支数
		//遍历该图的所有边,找到颜色不一样的,改变该连通分支out的值
		for(i=1;i<=N;i++)
		{
			if(v[i].empty())//如果没有该点
				continue;
			for(int j=0;j<v[i].size();j++)
			{
				if(paint[i]!=paint[v[i][j]])
					out[paint[i]]++;
			}
		}
		int res=0;
		count = 0;
		for(i=0;i<num;i++)
			if(out[i]==0)
			{
				count++;
				res = i;
			}
		if(count>1)
		{
			printf("0\n");
			continue;
		}
		printf("%d\n",conn[res].size());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值