Trajan算法(强连通+缩点)

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

问题概述:n所学校,它们通过单向边连接,如果A-->B表示A学校可以传递信息给B学校,那么问题来了,一:至少

要向几个学校传递信息,才能保证所有学校都能收到信息;二:至少要添加多少组关系,才能保证给任意一个学校

原始信息后,其他所有学校都能收到信息,输入第一个数表示有多少学校,后面n行,第i行第k个数表示i-->k(每行

输入到0结束)(POJ1236)

输入样例:                                      对应输出:

5                                                      1

2 4 3 0                                             2

4 5 0

0

0

1 0


两个顶点强连通:有向图G中,两个顶点可以通过某些路径互相到达

强连通图:该有向图中的任意两个顶点都强连通

强连通分量:非强连通有向图中的极大强连通子图(注意不是最大)

定理:

①当一个点既是强连通子图Ⅰ中的点,又是强连通子图Ⅱ中的点,则它是强连通子图Ⅰ∪Ⅱ中的点

②强连通分量一定由若干个环组成的

③在任何深度优先搜索中,同一强连通分量内的所有顶点均在同一棵深度优先搜索树中,也就是说,强连通分量一

定是有向图的某个深搜树子树


Trajan主要原理:

time[k]:第一次遍历到k点的时

low[k]:k点所在强连通分量子图中第一个被搜到的点的time值

vis[k]:k点是否已经遍历

栈:每当搜索到一个点时,将它压入栈,当这个点k所有子树全部搜索完毕时,如果low[k]==time[k],说明已经找到

了一个强连通分量,将k以及在k之上的元素弹全部出栈(它们全部属于一个强连通分量,且这个强连通分量不含其

它点)


搜索过程:

→当点k有与点c相连,如果此时(time[k]时)c不在栈中,搜索c点,当c点及其子树搜索完毕回溯后,计算出

low[k] min(low[k], low[c])

→当点k有与点c相连,如果此时(time[k]时)c在栈中,low[k] = min(low[k], time[c])

→搜索完毕后,栈内应该没有点了

期间保证:每个点每条边都只被搜索1次,且必须搜索1次,复杂度n+m,如果遍历完整个搜索树后某个点的time值

等于low值,则它是该搜索子树的根,这时,它以上(包括它自己)一直到栈顶的所有元素组成一个强连通分量,但

属于该强连通分量的所有的点low值不一定一致(但只有根节点满足low值与time值相等)


缩点过程:

→本质:将一个联通块作为一个点ltt[k]:k点属于第ltt[k]个联通块

→遍历一遍所有的边,如果边(u,v)中u和v属于不同联通块,则将它们两个所在的联通块连接在一起


题解:

设ain为缩点之后入度为0的点的个数,aout为缩点之后出度为0的点的个数,显然:这题第一个答案就是ain,第二

个答案当全图强连通时答案为0,否则为max(ain, aout)


#include
#include
#include
#include
using namespace std;
vector v[105], nv[105];
stack st;
int n, t, vis[105], inst[105], low[105], time[105], ltt[105], num, ain, aout, in[105], out[105];
void Trajan(int x);
int main(void)
{
	int i, j, x, temp;
	scanf("%d", &n);
	for(i=1;i<=n;i++)
	{
		while(scanf("%d", &x), x!=0)
			v[i].push_back(x);
	}
	t = num = 0;
	for(i=1;i<=n;i++)
	{
		if(vis[i]==0)
			Trajan(i);
	}
	for(i=1;i<=n;i++)
	{
		for(j=0;j


你可能感兴趣的:(#,有向图)