強聯通分量,聽着名字挺高大上的,一直聽別人說但是自己也不知道是什麼東西,昨天晚上,看見別人在學習這個東西,閒來無事的無就也學了學。
強連通分量在圖論問題中得到廣泛的應用,往往可以將有向圖縮點,得到一個 DAG,於是避免了原圖中可能有環造成後效性,可以在上面進行動態規劃求解。
強聯通分量究竟是幹什麼用的呢?我感覺吧他就是爲了節約時間,因爲有的題圖給的會比較大,如果我們按正常的方法去寫,一般都會超時的,既然圖這麼大,我們能不能把圖給簡化一下呢,簡單的說就是縮點,把一些可以放到因爲一起的點給他們變成一個點,這樣這個圖就會小一點了,那麼什麼樣的點纔算是相同的點呢,假如一個子圖的裏面的點的集合,任意兩個點都是可達的,那麼我們就稱這爲強聯通分量,就相當於是一個環。
強連通分量分解可以通過兩次簡單的DFS3實現。第一次DFS時,選取任意頂項點作爲起點,遍歷所看清來訪過的頂點,井在回謝前給頂點標號 (pot oder,後序遍歷)o對剩餘的未訪問過的頂
點,不斷重複上述過程。
完成標號後,越接近圖的尾部(搜索樹的葉子),頂點的標號越小。第二次DFS時,先將所有邊反向,然後以標號最大的頂點爲起點進行DFS。這樣DFS所遍歷的頂點集合就構成了一個強連通分量。之後,只要還有尚未訪問的頂點,就從中選取標號最大的頂點不斷重複上述過程。
下面給出模板,
#include<bits/stdc++.h>
using namespace std;
#define met(Q,QQ) memset(Q,QQ,sizeof(Q))
const int maxn=1e4+7;
vector<int> G[maxn];//存圖
vector<int> rG[maxn];//把邊反向後的圖
vector<int> vec;//後序遍歷順序的圖
bool vis[maxn];//訪問標記
int cmp[maxn];//所屬強聯通分量的拓撲序列
int n,m;
void add_edge(int from,int to)//存圖
{
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int k) //對圖進行dfs,並訪問到的點依次存入vec中,
{
vis[k]=true;
int kk=G[k].size();
for(int i=0;i<kk;i++)
{
int kkk=G[k][i];
if(!vis[kkk]) dfs(kkk);
}
vec.push_back(k);
}
void rdfs(int k,int cnt)//構建拓撲序
{
vis[k]=true;
cmp[k]=cnt;
int kk=rG[k].size();
for(int i=0;i<kk;i++)
{
int kkk=rG[k][i];
if(!vis[kkk]) rdfs(kkk,cnt);
}
}
int main()
{
scanf("%d %d",&n,&m);
int from,to;
for(int i=1;i<=m;i++)
{
scanf("%d %d",&from,&to);
add_edge(from,to);
}
met(vis,0);
for(int i=1;i<=n;i++)
{
if(!vis[i]) dfs(i);
}
met(vis,0);
int kk=vec.size();
int cnt=0;
for(int i=kk-1;i>=0;i--)
{
if(!vis[vec[i]])
{
cnt++;
int qq=vec[i];
rdfs(qq,cnt);
}
}
return 0;
}
感覺這個模板的最終的結果都存到了cmp數組中了,也就是圖的拓撲序,遇到題的時候只要靈活的運用cmp就行了