受欢迎的牛(有向图的强连通分量)

每一头牛的愿望就是变成一头最受欢迎的牛。

现在有 N 头牛,编号从 1 到 N,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。

这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。

你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

输入格式

第一行两个数 N,M;

接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。

输出格式

输出被除自己之外的所有牛认为是受欢迎的牛的数量。

数据范围

1≤N≤10^4,
1≤M≤5×10^4

输入样例:

3 3
1 2
2 1
2 3

输出样例:

1

样例解释

只有第三头牛被除自己之外的所有牛认为是受欢迎的。

分析题意后我们发现这道题的描述的符合题意的点和拓扑图中的出度为0的点极为相似

而且我们知道一个连通分量中的点都是可以互相到达的,也就是说如果可以找到唯一一个出度为0的连通分量,那么这个连通分量中的所有点都是满足题意的

这和tarjan算法的思路不谋而合,做完一遍tarjan之后我们就可以把互相可以到达的点都放到同一个连通分量之中

而且我们在做完一遍tarjan之后所有连通分量之间的关系是逆拓扑序,那么意味着我们在做完tarjan之后就不需要拓扑排序了,只需要把入度为0的连通分量统计一下就可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N = 100010;
using namespace std;
int n,m;
int h[N],ne[N],e[N],idx;
int dfn[N],low[N],timestamp;   //两个时间戳数组还有时间戳编号 
int stk[N],top;                //栈数组,栈元素编号 
bool in_stk[N];                 //判断是否在栈中 
int id[N],scc_cnt,s[N];        //连通分量编号,每个点属于哪个连通分量以及没每个连通分量有哪些点 
int dout[N];                   //每个点的出度,用来判断出度为0的点的个数 
void add(int a,int b)
{
	e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void tarjan(int u)
{
	dfn[u] = low[u] = ++timestamp;
	stk[++top] = u,in_stk[u] = 1;
	for(int i=h[u];~i;i=ne[i])
	{
		int j = e[i];
		if(!dfn[j]) 
		{
			tarjan(j);
			low[u] = min(low[u],low[j]);
		}
		else if(in_stk[j]) low[u] = min(low[u],dfn[j]);
	}
	
	if(dfn[u]==low[u])         //往栈里存元素+出栈 
	{
		++scc_cnt;
		int y;
		do
		{
			y = stk[top--];    //出栈  
			in_stk[y] = 0;     //标记点 
			id[y] = scc_cnt;
			s[scc_cnt]++;      //记录栈中元素个数 
		}while(y!=u);
	}
}

int main()
{
	cin>>n>>m;
	memset(h,-1,sizeof h);
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
	} 
	
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i);
	
	for(int i=1;i<=n;i++)
		for(int j=h[i];~j;j=ne[j])
		{
			int k = e[j];
			int a = id[i],b = id[k];
			if(a!=b) dout[a]++;
		} 
		
	int zcnt = 0,sum = 0;	
	for(int i=1;i<=scc_cnt;i++)
	{
		if(!dout[i])
		{
			zcnt++;
			sum += s[i];
		}
		if(zcnt>1) sum = 0;
	}	
	cout<<sum;
	return 0;
}

要加油!努力!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来计算有向图的最大连通分量大小。 下面是使用DFS计算有向图最大连通分量大小的Python代码: ```python def dfs(graph, visited, node, component): visited[node] = True component.append(node) for neighbor in graph[node]: if not visited[neighbor]: dfs(graph, visited, neighbor, component) def find_largest_strongly_connected_component(graph): n = len(graph) visited = [False] * n components = [] for node in range(n): if not visited[node]: component = [] dfs(graph, visited, node, component) components.append(component) largest_component = max(components, key=len) return len(largest_component) ``` 其中,`graph`是有向图的邻接表表示,`visited`是一个布尔数组,用于标记每个节点是否已经被访问过,`component`是一个列表,用于存储当前正在计算的连通分量中的所有节点。 在`find_largest_strongly_connected_component`函数中,我们首先遍历所有节点,对于每个未被访问过的节点,调用`dfs`函数计算以该节点为起点的连通分量,将其存储在`components`列表中。最后,我们从`components`中找到最大的连通分量,并返回其大小。 请注意,这里计算的是强连通分量,即在有向图中,对于任意两个节点u和v,如果u能够到达v,同时v也能够到达u,则称u和v是强连通的。如果需要计算弱连通分量,则需要将有向图转换为无向图,然后使用DFS或BFS计算最大连通分量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值