Trajan演算法(強連通+縮點)

Jaihk662發表於2016-08-12

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<stdio.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
vector<int> v[105], nv[105];
stack<int> 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<v[i].size();j++)
		{
			temp = v[i][j];
			if(ltt[i]!=ltt[temp])
				nv[ltt[i]].push_back(ltt[temp]);
		}
	}
	for(i=1;i<=num;i++)
	{
		for(j=0;j<nv[i].size();j++)
			in[nv[i][j]]++, out[i]++;
	}
	ain = aout = 0;
	for(i=1;i<=num;i++)
	{
		if(in[i]==0)
			ain++;
		if(out[i]==0)
			aout++;
	}
	printf("%d\n", ain);
	if(num==1)
		printf("0\n");
	else
		printf("%d\n", max(ain, aout));
	return 0;
}

void Trajan(int x)
{
	int i, temp;
	st.push(x);
	inst[x] = vis[x] = 1;
	low[x] = time[x] = ++t;
	for(i=0;i<v[x].size();i++)
	{
		temp = v[x][i];
		if(vis[temp]==0)
		{
			Trajan(temp);
			low[x] = min(low[x], low[temp]);
		}
		else if(inst[temp]==1)
			low[x] = min(low[x], time[temp]);
	}
	if(low[x]==time[x])
	{
		num++;
		while(st.empty()==0)
		{
			temp = st.top();
			st.pop();
			ltt[temp] = num;
			inst[temp] = 0;
			if(temp==x)
				break;
		}
	}
}


相關文章