【有向圖強連通分量(SCC)】

Tarjan算法(O(n+m))】

#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int n, pre[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
//sccno[1~n]標記每個點在第幾個SCC中,scc_cnt是SCC的計數,dfs_clock遞歸時間戳
vector<int> g[MAXN];
stack<int> s;

int dfs(int u)
{
	int lowu = pre[u] = ++dfs_clock, sz = g[u].size();
	s.push(u);
	rep(i,0,sz)
	{
		int v = g[u][i];
		if(!pre[v])
		{
			int lowv = dfs(v);
			lowu = min(lowu, lowv);
		}
		else if(!sccno[v]) lowu = min(lowu, pre[v]);
	}
	if(lowu == pre[u])//u的子孫最早只能回到u,說明找到一個SCC分量
	{
		int x;
		scc_cnt++;
		do{
			x = s.top();s.pop();
			sccno[x] = scc_cnt;
		}while(x != u);
		/*可以對這個強連通分量操作*/
	}
	return lowu;
}
void find_scc()
{
	dfs_clock = scc_cnt = 0;
	clc(pre,0);
	clc(sccno,0);
	repe(i,1,n) if(!pre[i]) dfs(i);
}


【Kosaraju算法(O(n+m)常數比Tarjan大)】

#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int sccno[MAXN], vis[MAXN], scc_cnt, n;
vector<int> g[MAXN], gt[MAXN], s;
//gt[]是g[]的相反邊的圖

void dfs1(int u)//獲取拓撲序
{
	vis[u] = true;
	int sz = g[u].size();
	rep(i,0,sz) if(!vis[g[u][i]]) dfs1(g[u][i]);
	s.push_back(u);
}
void dfs2(int u)
{
	sccno[u] = scc_cnt;
	int sz = gt[u].size();
	rep(i,0,sz) if(!sccno[gt[u][i]]) dfs2(gt[u][i]);
}
void find_scc()
{
	scc_cnt = 0;
	s.clear();
	clc(sccno,0);
	clc(vis,0);
	repe(i,1,n) if(!vis[i]) dfs1(i);
	per(i,n-1,0) if(!sccno[s[i]]) scc_cnt++, dfs2(s[i]);
}


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章