HDU 3836 Equivalent Sets(强连通+缩点,tarjan)

Equivalent Sets

                                               Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 104857/104857 K (Java/Others)
                                               Total Submission(s): 5049    Accepted Submission(s): 1837


Problem Description
To prove two sets A and B are equivalent, we can first prove A is a subset of B, and then prove B is a subset of A, so finally we got that these two sets are equivalent.
You are to prove N sets are equivalent, using the method above: in each step you can prove a set X is a subset of another set Y, and there are also some sets that are already proven to be subsets of some other sets.
Now you want to know the minimum steps needed to get the problem proved.
 

Input
The input file contains multiple test cases, in each case, the first line contains two integers N <= 20000 and M <= 50000.
Next M lines, each line contains two integers X, Y, means set X in a subset of set Y.
 

Output
For each case, output a single integer: the minimum steps needed.
 

Sample Input
4 0 3 2 1 2 1 3
 

Sample Output
4 2
Hint
Case 2: First prove set 2 is a subset of set 1 and then prove set 3 is a subset of set 1.
 

Source
 

Recommend
xubiao


题意:问添加几条边,能将图构成强连通图
思路:利用tarjan算法求出有几个强连通分量,再将这些强连通分量看成一个点,看下有几个出度为0或者入度为0的,输出那个最大的
(要使这些点点构成强连通图,那他就必须有一个出度和入度)

参考文献:

上代码:
/*求出强分量后缩点处理得到分支图,对分支图的每个
强连通分量统计出度和入度。把强连通分支当成一个点,
就叫做缩点。需要的边数就是:统计入度=0 的顶点数
和 出度=0 的顶点数,选择两者中较大的一个,才能确
保一个强连通图*/
/*
dfn[i]:深度优先搜索遍历时节点i被搜索的次序。
low[i]:节点i能够回溯到的最早位于栈中的节点。
visit[i]:标记几点i是否在栈中。
number:是强连通分支的个数
*/
#include<iostream>
#include<cmath>
#include<cstring>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<algorithm>
#define M 20010
using namespace std;
vector<int> G[M];
stack<int>q;
int dfn[M], low[M], in[M], out[M], belong[M];
bool visit[M];
int n, m, u, v, index, number;
void tarjan(int c)
{
	dfn[c] = low[c] = ++index;
	visit[c] = true;
	q.push(c);
	for (int i = 0; i < G[c].size(); i++)
	{
		int k = G[c][i];
		if (!dfn[k])
		{
			tarjan(k);
			low[c] = min(low[c], low[k]);//更新能找到的祖先
		}
		else if (visit[k])//这个条件很重要,这样可以避免已经确定在其他连通图的k,因为c到k的单向边而影响到c的low值
		{
			low[c] = min(low[c], dfn[k]);//能回到k被搜索时的次序
		}
	}
	if (dfn[c] == low[c])
	{
		int p;
		number++;//强连通分支的个数
		do {
			p = q.top();
			q.pop();
			belong[p] = number;
			visit[p] = false;
		} while (p != c);
	}
}
void solve()
{
	number = index = 0;
	memset(dfn, 0, sizeof(dfn));
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	memset(visit, false, sizeof(visit));
	for (int i = 1; i <= n; i++)
		if (!dfn[i])
			tarjan(i);
	if (number == 1)//特判,本身就是一个强连通图了
	{
		printf("0\n");
		return;
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < G[i].size(); j++)
		{
			int kk = G[i][j];
			if (belong[i] != belong[kk])//计算有几个出入度
			{
				out[belong[i]]++;
				in[belong[kk]]++;
			}
		}
	}
	int max1 = 0, max2 = 0;
	for (int i = 1; i <= number; i++)//计算出入度为0的个数
	{
		if (in[i] == 0)
			max1++;
		if (out[i] == 0)
			max2++;
	}
	printf("%d\n", max(max1, max2));
}
int main()
{
	while (scanf("%d%d", &n, &m) != EOF)
	{
		while (m--)
		{
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
		}
		solve();
		for (int i = 1; i <= n; i++)
			G[i].clear();
	}
	return 0;
}



阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38327682/article/details/77942166
文章标签: tarjan 缩点 强连通
个人分类: 连通性
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭