poj 1236 Network of Schools(强连通分量+缩点)

http://poj.org/problem?id=1236

题意:

有向关系体现在电脑之间可以通过网络单向的传输文件,并规定一旦有电脑存在该文件,那么所有它能传输的电脑都能在第一时间得到这个文件。给你n台电脑和每台电脑所直接传输的电脑编号。这里有两个问题:一是最少向网络中的几台电脑投放文件使得整个网络中的所有电脑能立刻获得该文件,二是最少向网络中添加几条单向连接可以使得这个网络中只要投放一个文件,所有电脑都能获得该文件。


思路:

该图中的文件具有瞬时性,传递性,可以很快发现其具有强连通的特征。对于图中的一个强连通分量,只要向其中的一个点投放文件,则整个分量内的点都能瞬间收到这份文件,所有可以将一个分量压缩成一个点,这样将图中的所有强连通分量都压缩成若干个点,就变成所谓的有向无环树DAG。


这样问题就简单了,解决第一个问题,只需求出DAG中入度为0的缩点的个数即可。因为该所点入度为0,它就没有文件的来源,于是该缩点就是投放文件的目标。解决第二个问题,是在问加入几条边使得原图变为强连通图。对于DAG,入度为0 的点为树根,出度为0 的点为叶子,而强连通的一个最重要的性质是每个点的出度和入度都不为0,但是显然树根和叶子都是不满足条件的点,因此就是在树根和叶子之间连上足够的边使得原图变为强连通,令f为树根数,g为叶子数,则答案为max(f,g)。


特别注意当原图原本就是强连通时输出 1 0.


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;

const int maxn = 110;
vector <int> edge[maxn];//原图
vector <int> edge2[maxn];//缩点后的图
int n;
stack <int> st;
int vis[maxn],dfn[maxn],low[maxn],instack[maxn],index,connec;
int set[maxn],num[maxn];

void tarjan(int u)
{
	dfn[u] = low[u] = ++index;
	instack[u] = 1;
	vis[u] = 1;
	st.push(u);

	for(int i = 0; i < (int)edge[u].size(); i++)
	{
		int v = edge[u][i];
		if(!vis[v])
		{
			tarjan(v);
			low[u] =  min(low[u],low[v]);
		}
		else if(instack[v])
			low[u] = min(low[u],dfn[v]);
	}

	if(dfn[u] == low[u])
	{
		connec++;//连通分量数增1
		while(1)
		{
			int tmp = st.top();
			st.pop();
			instack[tmp] = 0;
			set[tmp] = connec;
			num[connec]++;
			if(tmp == u)
				break;
		}
	}
}

int in[maxn],out[maxn],inlink[maxn];
int ind,outd;
void creat_DAG()
{
	memset(in,0,sizeof(in));
	memset(out,0,sizeof(out));
	for(int u = 1; u <= n; u++)
	{
		for(int i = 0; i < (int)edge[u].size(); i++)
		{
			int v = edge[u][i];
			if(set[u] != set[v])
			{
				edge2[ set[u] ].push_back( set[v] );
				out[ set[u] ]++;
				in[ set[v] ]++;
			}
		}
	}
}

void solve()
{
	ind = outd = 0;
	for(int i = 1; i <= connec; i++)
	{
		if(in[i] == 0)
			ind++;
		else if(out[i] == 0)
			outd++;
	}
	printf("%d\n%d\n",ind,max(ind,outd));
}

int main()
{
	scanf("%d",&n);
	int x;
	for(int i = 1; i <= n; i++)
	{
		edge[i].clear();
		edge2[i].clear();
		while(1)
		{
			scanf("%d",&x);
			if(x == 0) break;
			edge[i].push_back(x);
		}
	}
	memset(vis,0,sizeof(vis));
	memset(dfn,0,sizeof(vis));
	memset(low,0,sizeof(vis));
	memset(instack,0,sizeof(vis));
	while(!st.empty()) st.pop();
	index = 0;
	connec = 0;
	for(int i = 1; i <= n; i++)
		if(!vis[i])
			tarjan(i);
	creat_DAG();
	if(connec == 1)
		printf("1\n0\n");//只有一个连通块的时候输出 1 0;
	else solve();
	return 0;
}




你可能感兴趣的:(Tarjan,强连通分量,缩点)