E. Reachability from the Capital (tarjan缩点+分析)

题目

题意:

    给定一张有向图,可以在上面建边,输出构建尽可能少的边使得从给定的起点开始可以到达图上的任意一点。
     1 ≤ n ≤ 5000 , 0 ≤ m ≤ 5000 , 1 ≤ s ≤ n 1≤n≤5000,0≤m≤5000,1≤s≤n 1n5000,0m5000,1sn

分析:

    我们思考该如何建边,显然应该向一个任何点都无法到达的点建一条边,即这个点的入度为0。这样我们就可以利用dfs来更新可达的点,当某个点入度为0且没被更新时,建一条边到这个点,继续更新未达的点。由于有向图存在强连通分量,所以还需要tarjan+缩点处理一下。

#include <iostream>
#include <vector>
#include <stack>
#include <cstring> 
using namespace std;

vector<int> g[5005],g_new[5005];
int vis[5005],in[5005];
int dfn[5005],low[5005];
int scc[5005],c = 1;   
stack<int> s;
int cnt = 0;

void dfs(int x)
{
	vis[x] = 1;
	for (int i = 0; i < g_new[x].size(); i++)
	{
		int t = g_new[x][i];
		if( vis[t] ) continue;
		dfs(t); 
	}	
}

void tarjan(int x)  
{
	dfn[x] = low[x] = ++cnt;
	s.push(x);
	vis[x] = 1;
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( !dfn[t] ) 
		{
			tarjan(t);
			low[x] = min(low[x],low[t]); 
		}else if( vis[t] ) 
		{
			low[x] = min(low[x],low[t]);   
		}
	} 
	if( dfn[x] == low[x] )   
	{ 						 
		vis[x] = 0;
		scc[x] = c;          
		while( s.top() != x )     
		{
			vis[s.top()] = 0;
			scc[s.top()] = c;  
			s.pop(); 
		}
		c ++;     
		s.pop();   
	}
}

void compress(int n)    
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < g[i].size(); j++)    
		{
			int t = g[i][j];
			if( scc[i] != scc[t] )     
			{
				g_new[scc[i]].push_back(scc[t]);   
				in[scc[t]] ++;
			}
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n,m,begin;
	cin >> n >> m >> begin;
	for (int i = 1; i <= m; i++)
	{
		int x,y;
		cin >> x >> y;
		g[x].push_back(y); 
	}
	for (int i = 1; i <= n; i++)
	{
		if( !dfn[i] ) tarjan(i); 
	}
	compress(n); 
	memset(vis,0,sizeof(vis));
	int ans = 0;
	dfs(scc[begin]);
	for (int i = 1; i < c; i++)
	{
		if( in[i] == 0 && vis[i] == 0 )
		{
			ans ++;
			dfs(i);
		}
	}
	cout << ans << '\n';
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值