poj 2186 Popular Cows(kosaraju求联通分量)

这是一篇关于解决POJ 2186问题的博客,内容涉及有向图的 Kosaraju 算法,旨在找出图中的联通分量,并分析在何种情况下最多有多少个点可以由图中其他所有点到达。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址

题目大意:给出一个有n个点,m条边的有向图,求图中最多有多少个点能由除该点之外的所有到达

解题思路:先求出图中的联通分量,将其缩为一个点,并记录联通分量个数,每个联通分量的点数及其出度,若整个图中只有一个联通分量的出度为0,说明其他联通分量与其相连(图是联通的),或者整个图是一个联通分量

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <string>
#include <map>
#include <stack>
#include <list>
#include <set>

using namespace std;

const int maxn=10010;

int ord[maxn];//正向搜索,顶点的编号
bool vis[maxn];//标记顶点是否遍历过
int out[maxn];//转化为DAG以后的每个缩点的出度
int belong[maxn];//当前顶点属于哪个集合,相当于染色,当前顶点被染成了什么颜色
int ans[maxn];//每种颜色包括多少顶点,也就是强联通分量点的个数
int color;//代表不同的颜色,即不同的联通分量
int no;//正向搜索排序的编号(1---n)
int n,m;//顶点数和边数
vector<int> g[maxn],gre[maxn];//存储正向图和逆图

void dfs1(int u)//从当前u顶点开始DFS
{
	vis[u] = 1;
	for(int i = 0; i < g[u].size(); i++)
	{
		int v = g[u][i];
		if(!vis[v]) dfs1(v);
	}
	ord[no++]=u;//为每个顶点编号
}

void dfs2(int u)
{
	vis[u] = 1;
	belong[u] = color;//当前顶点u被染成了color
	for(int i = 0; i < gre[u].size(); i++)
	{
		int v = gre[u][i];
		if(!vis[v])
		{
			ans[color]++;
			dfs2(v);
		}
	}
}

int kosaraju()
{
	color = 1,no = 1;
	for(int i = 1; i <= n; i++)//初始化每个点为1个联通分量,即联通分量的点的个数为1
		ans[i]=1;
	memset(out,0,sizeof(out));//初始化每个联通分量的出度
	memset(vis,0,sizeof(vis));
	for(int i = 1; i <= n; i++)//搜索每个连通分量并对其进行正序搜索编号
		if(!vis[i]) dfs1(i);
	memset(vis,0,sizeof(vis));//一定要再次进行遍历初始化
	for(int i = no-1; i >= 1; i--)//编好号以后,从排号最大的开始搜索
	{
		int v = ord[i];//最后入栈的点
		if(!vis[v])
		{
			dfs2(v);
			color++;//记录联通分量个数
		}
	}
	//构造DAG
	for(int i = 1; i <= n; i++)
	{
		for(int j = 0; j < g[i].size(); j++)
		{
			if(belong[i] == belong[g[i][j]]) //若2个点属于同一个联通分量
				continue;
			out[belong[i]]++;//若有边相连的2个点不是同一个联通分量,则该联通分量的出度+1
		}
	}
	//查找每种颜色出度为0的点或缩点,out[i]代表第i种颜色的点或缩点的出度,出度为0唯一,输出该颜色集合中点的个数ans[i],否则输出0
	int ret = 0;//记录出度为0的联通分量的点的个数,若有多于2个联通分量的出度为0,表示此图不连通
	int cnt = 0;//记录出度为0的个数,若出度为0的联通分量只有1个,即被其他点连通的点的个数为该联通分量的点数
	for(int i = 1;i < color; i++)//联通分量的个数
	{
		if(!out[i])//若出度为0
		{
			cnt++;
			ret = ans[i];
		}
		if(cnt == 2)//这两个if不能写颠倒,切记啊!
		{
			ret = 0;
			break;
		}
	}
	return ret;
}

int main()
{
    int result;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int u,v;
		for(int i = 1;i <= m; i++)
		{
			scanf("%d%d",&u,&v);
			g[u].push_back(v);
			gre[v].push_back(u);
		}
		result = kosaraju();
		printf("%d\n",result);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值