【CCF 201509-4】高速公路(强连通分量)

版权声明: https://blog.csdn.net/leelitian3/article/details/82716527

题目抽象

求有多少个结点对能够互相到达

大致思路

思路一【50分】:对每个结点DFS,求传递闭包,时间为O(V*E),代码简单,但是超时

思路二【100分】:计算图的强连通分量(SCC),各个分量里面的点都是可以相互到达的

SCC算法:O(V+E)

强连通分量(Strongly Connected Components)

①DFS求拓扑序(按u.f升序排列)

②将图转置,按拓扑序的逆序DFS,得到的就是SCC

C++满分代码(带注释)

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#include <list>
#define endl "\n"
using namespace std;

vector<int> topo;
vector<list<int> > adj,tmp;
int n,m,a,b;
bool vis[10005];
vector<int> cnt;   //各个SCC的结点个数 

void dfs1(int x)
{
	for(list<int>::iterator p = adj[x].begin(); p != adj[x].end(); ++p)
	{
		if(vis[*p] == 0)
		{
			vis[*p] = 1;
			dfs1(*p);
		}
	}
	topo.push_back(x);   //完成搜索,即结点变为黑色时,加入拓扑序 
}

void dfs2(int x)
{
	for(list<int>::iterator p = tmp[x].begin(); p != tmp[x].end(); ++p)
	{
		if(vis[*p] == 0)
		{
			vis[*p] = 1;
			++cnt.back();   //增加当前SCC的结点个数 
			dfs2(*p);
		}
	}
}

inline int compute(int a)   //组合数C(n, 2) 
{
	if(a<2) return 0;
	return a*(a-1)/2;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin>>n>>m;
	adj.assign(n+1,list<int>());
	tmp.assign(n+1,list<int>());
	for(int i=0; i<m; ++i)       //建立邻接表adj和邻接表的转置tmp 
	{
		cin>>a>>b;
		adj[a].push_back(b);
		tmp[b].push_back(a);
	}

	memset(vis,0,sizeof(vis));   //第一次dfs,记录"拓扑序" 
	for(int i=1; i<=n; ++i)
	{
		if(vis[i]==0)
		{
			vis[i] = 1;
			dfs1(i);
		}
	}

	memset(vis,0,sizeof(vis));   //第二次dfs,求SCC 
	for(int i=n-1; i>=0; --i)
	{
		int node = topo[i];
		if(vis[node]==0)
		{
			vis[node] = 1;
			cnt.push_back(1);
			dfs2(node);
		}
	}

	int sum = 0;
	for(int i=0; i<cnt.size(); ++i)  //计算答案 
		sum += compute(cnt[i]);
	cout<<sum;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/leelitian3/article/details/82716527