POJ 2186 强连通图(模板)

难度的话在于不好转换成图来思考。
其实这里的有序对,a认为b,b认为c,并且具有传递性我们就可以考虑用一条链把abc穿起来,从a能到c表示a对c崇拜,就类似与拓扑结构,入度为0的点没有人崇拜,随着链走,任意节点都是被以前的所有节点崇拜,那么最后一个节点就被所有的崇拜。
这是一条的链的情况。
这道题是多条,也可以这么思考。
假设,a被所有牛崇拜,a崇拜b,那么b肯定也被其他所有牛崇拜,因为b也崇拜a,这样就做成了一个环,我们将强连通图分量抽象成点,那么,这就转换成一条链了,问题是,最后一条链有多少个点在里面。
注意这道题坑点在于强连通图最后一个可能不是答案,因为可能这条链并不是联通的,所以拓扑排序过后,还要继续反向遍历一遍,看是不是这个强连通分量能够到达所有的点。(因为强连通图第二次dfs时是反向图,所以我们得到的拓扑排序最大的点,其实是叶子,正向进行的最后一个点。即dag的最后一个点,所以需要rdfs)

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
typedef pair<int, int> pir;
int n, m;
vector<int >g[10005];
vector<int >r[10005];
vector<int >vs;
bool used[10005];
int cmp[10005];
void addedge(int f, int s)
{
	g[f].push_back(s);
	r[s].push_back(f);
}
void dfs(int v)
{
	used[v] = true;
	up(i, 0, g[v].size())
	{
		if (!used[g[v][i]])dfs(g[v][i]);
	}
	vs.push_back(v);
}//正向后续遍历,叶子节点先到,即越是链的末尾,越先遍历到,这样才能做强连通图的判定
void rdfs(int v, int k)
{
	used[v] = true;
	cmp[v] = k;
	up(i, 0, r[v].size())
	{
		if (!used[r[v][i]])rdfs(r[v][i], k);
	}
}
int scc()//强连通分量的计算
{
	memset(used, 0, sizeof(used));
	vs.clear();
	upd(v, 1, n)
		if (!used[v])dfs(v);//先正向遍历
	memset(used, 0, sizeof(used));
	int k = 0;
	dwd(i, vs.size() - 1, 0)
		if (!used[vs[i]])rdfs(vs[i], k++);//反向遍历并且拓扑
	return k-1;//减一,返回拓扑排序最大块。
}
int main()
{
	cin >> n >> m;
	int x, y;
	up(i, 0, m)
	{
		scanf("%d%d", &x, &y);
		addedge(x, y);
	}
	int temp = scc();
	int ans = 0;
	int u;
	upd(i, 1, n)
		if (cmp[i] == temp)//查看属于这个块的人数
		{
			u = i;
			ans++;
		}
	memset(used, 0, sizeof(used));//重新从顶点往回遍历
	rdfs(u, 0);
	upd(i, 1, n)
		if (!used[i])//如果有没有被访问到的,说明有独立块,肯定就是互相看不上了
			ans = 0;
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值