The Cow Prom(POJ 3180)---求强连通分量模板题

题目链接

题目描述

The N (2 <= N <= 10,000) cows are so excited: it’s prom night! They are dressed in their finest gowns, complete with corsages and new shoes. They know that tonight they will each try to perform the Round Dance.
Only cows can perform the Round Dance which requires a set of ropes and a circular stock tank. To begin, the cows line up around a circular stock tank and number themselves in clockwise order consecutively from 1…N. Each cow faces the tank so she can see the other dancers.
They then acquire a total of M (2 <= M <= 50,000) ropes all of which are distributed to the cows who hold them in their hooves. Each cow hopes to be given one or more ropes to hold in both her left and right hooves; some cows might be disappointed.
For the Round Dance to succeed for any given cow (say, Bessie), the ropes that she holds must be configured just right. To know if Bessie’s dance is successful, one must examine the set of cows holding the other ends of her ropes (if she has any), along with the cows holding the other ends of any ropes they hold, etc. When Bessie dances clockwise around the tank, she must instantly pull all the other cows in her group around clockwise, too. Likewise,
if she dances the other way, she must instantly pull the entire group counterclockwise (anti-clockwise in British English).
Of course, if the ropes are not properly distributed then a set of cows might not form a proper dance group and thus can not succeed at the Round Dance. One way this happens is when only one rope connects two cows. One cow could pull the other in one direction, but could not pull the other direction (since pushing ropes is well-known to be fruitless). Note that the cows must Dance in lock-step: a dangling cow (perhaps with just one rope) that is eventually pulled along disqualifies a group from properly performing the Round Dance since she is not immediately pulled into lockstep with the rest.
Given the ropes and their distribution to cows, how many groups of cows can properly perform the Round Dance? Note that a set of ropes and cows might wrap many times around the stock tank.

输入格式

Line 1: Two space-separated integers: N and M
Lines 2…M+1: Each line contains two space-separated integers A and B that describe a rope from cow A to cow B in the clockwise direction.

输出格式

Line 1: A single line with a single integer that is the number of groups successfully dancing the Round Dance.

输入样例

5 4
2 4
3 5
1 2
4 1

输出样例

1

分析

题目大意是求强连通分量,并输出强连通分量内顶点数量大一1的个数?
直接套用tarjan算法或者Korasaju算法模板即可,以下是源码。

源程序

tarjan算法

#include <cstdio>
#include <cstring>
#include <stack>
#define MAXN 10005
using namespace std;
struct Edge{	//链式前向星 
	int v,next;
	Edge(){};
	Edge(int _v,int _next){
		v=_v,next=_next;
	}
}edge[MAXN*5]; 
int EdgeCount,head[MAXN];
int n,m,cnt,color;
int dfn[MAXN],low[MAXN],num[MAXN],sum[MAXN];
bool vis[MAXN];
stack <int> s;
void addEdge(int u,int v)	//链式前向星建图 
{
	edge[++EdgeCount]=Edge(v,head[u]);
	head[u]=EdgeCount;
}
int read()	//快读 
{
	int sum=0;
	char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){
		sum=sum*10+c-'0';
		c=getchar();
	} 
	return sum;
}
void init()	//初始化 
{
	memset(head,0,sizeof(head));	
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(num,0,sizeof(num));
	memset(sum,0,sizeof(sum));
	memset(vis,false,sizeof(vis));
	EdgeCount=cnt=color=0;
}
void tarjan(int u)
{
	dfn[u]=low[u]=++cnt;	//打上时间戳 
	vis[u]=true;	//标记入栈 
	s.push(u);	//入栈
	for(int i=head[u];i;i=edge[i].next){	//遍历相邻节点 
		int v=edge[i].v;
		if(!vis[v]){	//未被访问过
			tarjan(v);
			low[u]=min(low[u],low[v]); 
		}
		else if(vis[u])					//已经访问过但在栈中 
			low[u]=min(low[u],dfn[v]);	//在判断强连通分量是用dfn还是low其实没影响
	}									//但在求割点时,用low[v]比较会漏掉割点	
	if(dfn[u]==low[u]){	 //满足强连通分量要求
		color++;	//记录强连通分量个数
		while(1){
			int tmp=s.top();s.pop();
			vis[tmp]=false;
			num[tmp]=color;		//标记所属强连通分量 
			if(tmp==u)break;
		} 
	} 
}
int main()
{
	while(~scanf("%d%d",&n,&m)){
		init(); 
		for(int i=1;i<=m;i++){
			int u=read(),v=read();
			addEdge(u,v);
		}
		for(int i=1;i<=n;i++)
			if(!dfn[i])
				tarjan(i);	
		int ans=0;
		for(int i=1;i<=n;i++)	//统计各强连通分量内节点个数 
			sum[num[i]]++;	
		for(int i=1;i<=color;i++)
			if(sum[i]>1)ans++;
		printf("%d\n",ans);
	}
}

Kosaraju算法

//#include <bits/stdc++.h> 
#include <cstdio>
#include <cstring>
#include <stack>
#define MAXN 10005
using namespace std;
struct Edge{	//链式前向星 
	int v,next;
	Edge(){};
	Edge(int _v,int _next){
		v=_v,next=_next;
	}
}; 
struct GNode{
	int EdgeCount,head[MAXN];
	Edge edge[MAXN*5];
	void clear(){
		memset(head,0,sizeof(head));
		EdgeCount=0;
	}
	void addEdge(int u,int v){	//链式前向星建图 
		edge[++EdgeCount]=Edge(v,head[u]);
		head[u]=EdgeCount;
	}
}; 
GNode G,GT;
bool vis[MAXN];
int n,m,cnt,color,dfn[MAXN],num[MAXN],sum[MAXN];
stack <int> s;
int read()	//快读 
{
	int sum=0;
	char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){
		sum=sum*10+c-'0';
		c=getchar();
	} 
	return sum;
}
void init()	//初始化 
{
	memset(dfn,0,sizeof(dfn));
	memset(num,0,sizeof(num));
	memset(sum,0,sizeof(sum));
	G.clear();
	GT.clear();
}
void dfs1(int u)	//第一次深搜记录搜索完成时间 
{
	vis[u]=true;
	for(int i=G.head[u];i;i=G.edge[i].next){
		int v=G.edge[i].v;
		if(!vis[v])	//没有访问过 
			dfs1(v);
	}
	dfn[++cnt]=u;	//记录完成时间 
}
void dfs2(int u)	//第二次深搜记录强连通分量 
{
	vis[u]=true;
	num[u]=color;
	for(int i=GT.head[u];i;i=GT.edge[i].next){
		int v=GT.edge[i].v;
		if(!vis[v])
			dfs2(v);
	}
} 
void Kosaraju()
{
	/*第一次深搜*/ 
	cnt=0;	//时间戳 
	memset(vis,false,sizeof(vis));
	for(int i=1;i<=n;i++)
		if(!vis[i])
			dfs1(i);
	
	/*第二次深搜*/
	color=0;	//记录强连通分量个数
	memset(vis,false,sizeof(vis));
	for(int i=n;i>=1;i--){
		if(!vis[dfn[i]]){
			color++;
			dfs2(dfn[i]);
		}
	} 
}
int main()
{
	while(~scanf("%d%d",&n,&m)){
		init(); 
		for(int i=1;i<=m;i++){
			int u=read(),v=read();
			G.addEdge(u,v);
			GT.addEdge(v,u);
		}
		Kosaraju();
		int ans=0;
		for(int i=1;i<=n;i++)	//统计各强连通分量内节点个数 
			sum[num[i]]++;	
		for(int i=1;i<=color;i++)
			if(sum[i]>1)ans++;
		printf("%d\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值