POJ 2186 Popular Cows(強連通分量縮點,Tarjan演算法)
【題目連結】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16578
【解題報告】
給你一個有向圖,問你有多少個點可以被其它所有點訪問到。
因為圖中存在環,而環裡的節點可以互相訪問到,所以我們應當將一個環縮為一個點(這是不影響對題目的分析的)。
這樣這個有向圖就變成了DAG(有向無環圖)。
容易看出,在這個圖中,如果只存在一個節點出度為0,那麼它可以被其它所有結點訪問到。如果存在多個節點出度為0,那麼本題就無解。(n個點被分為了多棵樹,每顆樹無法相互訪問)
那麼這道題的核心就在於縮點這個步驟。
關於縮點所需要的tarjan演算法,在下面給出的連結裡有相當清晰的說明,這裡不再贅述。其它說明請參考程式碼註釋。
ps:其實看懂了tarjan演算法之後會覺得這個演算法思路很易懂程式碼也相當簡潔的。不過網上的資料往往直接描述演算法,而缺乏對本身的分析,所以看起來很難懂。找了一些自己覺得不錯的資料貼在後面,希望能幫助讀者理解求解強連通分量的演算法。
【參考資料】
《 深度理解鏈式前向星 》–Acdreamers
http://blog.csdn.net/acdreamers/article/details/16902023
《處理SCC(強連通分量問題)的Tarjan演算法》–comzyh
https://comzyh.com/blog/archives/517/
《有向圖強連通分量的Tarjan演算法》–byvoid
https://www.byvoid.com/blog/scc-tarjan/
【參考程式碼】
#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
#include<cmath>
using namespace std;
const int maxn=1e4+50;
const int maxm=5e4+50;
int head[maxn],LOW[maxn],DFN[maxn],id[maxn],cnt[maxn];
bool mark[maxn];
int N,M,time=0,scc=0;
stack<int>sta;
struct edge_t{
int to,next;
}edge[maxm];
/*
DFN(u)為節點u搜尋的次序編號(時間戳)
Low(u)為u或u的子樹能夠追溯到的最早的棧中節點的次序號
當DFN(u)=Low(u)時,以u為根的搜尋子樹上所有節點是一個強連通分量。
*/
void tarjan( int u )
{
DFN[u]=LOW[u]=++time;
sta.push(u);
mark[u]=true;
for( int k=head[u]; k!=-1; k=edge[k].next )
{
int v=edge[k].to;
if(!DFN[v])
{
tarjan(v);
LOW[u]=min( LOW[u],LOW[v] );
}
else if(mark[v])//v已經進入過棧但是還沒出棧
{
LOW[u]=min(LOW[u],DFN[v]);
}
}
if(DFN[u]==LOW[u])
{
scc++;
int v;
do
{
v=sta.top();
sta.pop();
id[v]=scc; //v這個節點屬於哪個scc
mark[v]=false;
cnt[scc]++; //標記每個scc包含的節點數量
}
while(u!=v);
}
}
void solve()
{
memset(mark,0,sizeof mark);//用來標記節點是否在棧裡
memset(DFN,0,sizeof DFN);
for( int i=1; i<=N; i++ ) if(!DFN[i]) tarjan(i);
memset(mark,0,sizeof mark);//用來標記某個scc是否有出度
for( int i=1; i<=N; i++ )
{
for( int k=head[i]; k!=-1; k=edge[k].next )
{
if(id[i]!=id[edge[k].to])
mark[id[i]]=true;
}
}
int pos=0,lab=0;
for( int i=1; i<=scc; i++ )if(!mark[i])
{
lab++;
pos=i;
}
if(lab>1)printf("0\n");
else printf("%d\n",cnt[pos]);
}
int main()
{
scanf("%d%d",&N,&M);
memset(head,-1,sizeof head);
memset(cnt,0,sizeof cnt);//記錄每個scc有多少個節點
for( int i=0; i<M; i++ )
{
int a,b;
scanf("%d%d",&a,&b);
edge[i].to=b; //利用了類似前向星的儲存方法
edge[i].next=head[a];
head[a]=i;
}
solve();
return 0;
}
相關文章
- 【模板】tarjan 強連通分量縮點
- 強連通分量及縮點tarjan演算法解析演算法
- 圖之強連通、強連通圖、強連通分量 Tarjan演算法演算法
- 強連通分量(Tarjan演算法)演算法
- 圖論複習之強連通分量以及縮點—Tarjan演算法圖論演算法
- 強連通分量與縮點(Tarjan演算法)(洛谷P3387)演算法
- Tarjan演算法(強連通分量分解)演算法
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- Tarjan演算法求強連通分量總結演算法
- 強連通分量-tarjan演算法模板詳解演算法
- HDU 2767 Proving Equivalences Tarjan 強連通縮點UI
- 演算法學習之路|強連通分量+縮點演算法
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- Tarjan演算法三大應用之強連通分量演算法
- 強連通------tarjan演算法詳解及與縮點聯合運用演算法
- 【演算法學習】tarjan 強連通、點雙、邊雙及其縮點 重磅來襲!!!!演算法
- 強連通分量及縮點 演算法解析及例題演算法
- Tarjan 求有向圖的強連通分量
- Trajan演算法(強連通+縮點)演算法
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- 20行程式碼實現,使用Tarjan演算法求解強連通分量行程演算法
- Tarjan演算法_縮點演算法
- HDU2767Proving Equivalences[強連通分量 縮點]UI
- 強連通演算法--Tarjan個人理解+詳解演算法
- tarjan演算法求scc & 縮點演算法
- 連通圖與Tarjan演算法演算法
- 強連通分量
- POJ 1523-SPF(Tarjan演算法-關節點)演算法
- 【筆記】tarjian演算法 求強連通分量筆記演算法
- POJ 1523 SPF (無向圖求割點 tarjan演算法)演算法
- 連通圖演算法詳解之① :Tarjan 和 Kosaraju 演算法演算法
- Tarjan縮點題單 刷題題解
- 強連通圖的演算法演算法
- POJ2387-Til the Cows Come Home
- tarjan縮點-受歡迎的牛-筆記筆記
- POJ2387 Til the Cows Come Home【最短路 Dijkstra演算法】演算法
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- POJ 3592 Instantaneous Transference 圖論演算法tarjan+spfa圖論演算法