日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Tarjan算法 (强联通分量 割点 割边)

發(fā)布時(shí)間:2025/3/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Tarjan算法 (强联通分量 割点 割边) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

變量解釋:
low 指當(dāng)前節(jié)點(diǎn)在同一強(qiáng)連通分量(或環(huán))能回溯到的dfn最小的節(jié)點(diǎn)
dfn 指當(dāng)前節(jié)點(diǎn)是第幾個(gè)被搜到的節(jié)點(diǎn)(時(shí)間戳)
sta 棧
vis 是否在棧中
ans 指強(qiáng)連通分量的數(shù)量
top 棧頂

1.求強(qiáng)連通分量
定義:如果兩個(gè)頂點(diǎn)可以相互通達(dá),則稱兩個(gè)頂點(diǎn)強(qiáng)連通(strongly connected)。如果有向圖G的每?jī)蓚€(gè)頂點(diǎn)都強(qiáng)連通,稱G是一個(gè)強(qiáng)連通圖。有向圖的極大強(qiáng)連通子圖,稱為強(qiáng)連通分量(strongly connected components)。

算法:在有向圖中從一點(diǎn)(u)開始dfs,記錄dfn,搜到一個(gè)已在棧中的點(diǎn)(v)時(shí)用dfn[v] (low[v]也行,但只有求強(qiáng)連通分量時(shí)可以別的只能用dfn[v]) 嘗試更新low[u],并在回溯時(shí)更新沿路的點(diǎn)的low值,走到low值與dfn相同的點(diǎn)時(shí)記錄這個(gè)強(qiáng)連通分量即可。
也就是說:在同一個(gè)強(qiáng)連通分量中所有點(diǎn)low值相同,也就是有一個(gè)代表點(diǎn)(代表點(diǎn)即所有點(diǎn)的low值即強(qiáng)連通分量中dfn值最小的點(diǎn))
時(shí)間復(fù)雜度為O(E+V)

code

void tarjan(int u){dfn[u]=low[u]=++cnt;//初始化一點(diǎn)的dfn和lowsta[++top]=u,vis[u]=true;//入棧for(int i=head[u];i;i=edge[i].next){//鄰接表int v=edge[i].to;if(!dfn[v]){//如果沒走過tarjan(v);low[u]=min(low[u],low[v]);//回溯過程時(shí)low值傳遞}else if(vis[v]) low[u]=min(low[u],dfn[v]); //low[v]也行 用代表點(diǎn)更新}if(dfn[u]==low[u]) {//如果是代表點(diǎn) 記錄并出棧ans++;//記錄強(qiáng)連通分量個(gè)數(shù)while(sta[top]!=u){vis[sta[top]]=false;top--;}vis[sta[top]]=false;top--;}return ; }

2.求無向圖的割點(diǎn)與割邊
割點(diǎn):在無向圖中,如果將一個(gè)點(diǎn)以及所有連接該點(diǎn)的邊都去掉,圖就不再連通,那么這個(gè)點(diǎn)就叫做這個(gè)圖的一個(gè)割點(diǎn)。
割邊:在無向圖中,如果將一條邊去掉,圖就不再連通則稱這條邊為圖的一個(gè)割邊。

求割點(diǎn):如果一個(gè)點(diǎn)(u)所連接的幾個(gè)節(jié)點(diǎn)(v)的low值大或等于此節(jié)點(diǎn)(u)的dfn值時(shí)說明之后的節(jié)點(diǎn)(v)無法連接到比此點(diǎn)(u)更早的點(diǎn)上,則說明這個(gè)節(jié)點(diǎn)(u)是一個(gè)割點(diǎn)。PS:根節(jié)點(diǎn)需特判,當(dāng)根節(jié)點(diǎn)在dfs樹有兩個(gè)或更多個(gè)子樹時(shí)則說明根節(jié)點(diǎn)是割點(diǎn)

求割邊:與割點(diǎn)類似,如果一個(gè)點(diǎn)(u)的dfn值大于(不能等于,否則不一定)和它連接的一個(gè)節(jié)點(diǎn)(v)的low值,則說明這條邊(uv)為圖的一個(gè)割邊

變量解釋:
sum 指總共有幾個(gè)割點(diǎn)(邊)

割點(diǎn)code

void cutpoint(int u){int fl=0;//為特判準(zhǔn)備dfn[u]=low[u]=++cnt;//初始化for(int i=head[u];i;i=edge[i].next){//用鄰接表,下同int v=edge[i].to;if(!dfn[v]){cutpoint(v);low[u]=min(low[u],low[v]);if(u!=root&&low[v]>=dfn[u]&&!cpoint[u]) sum++,cpoint[u]=1;//不是根節(jié)點(diǎn)&&v的low值>=u的dfn值&&此點(diǎn)沒有算過if(u==root) fl++;//此時(shí)特判++}low[u]=min(low[u],dfn[v]); }if(fl>=2&&!cpoint[u]) sum++,cpoint[u]=1;//根節(jié)點(diǎn)若有兩棵子樹則是割點(diǎn) }

割邊code

void cutedge(int u,int f){dfn[u]=low[u]=++cnt;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){cutedge(v,u);low[u]=min(low[u],low[v]);if(dfn[u]<low[v]) cedge[++sum]=i;//記錄邊的序號(hào)}else if(v!=f) low[u]=min(low[u],dfn[v]); //只有當(dāng)v不是u的上一個(gè)節(jié)點(diǎn)時(shí)可行} }

完整模板code:
ps:這里就不打注釋了,核心就在上面的部分里

#include<cstdio> #include<iostream> #include<cstring> using namespace std;const int MAX=1000010; int n,m,cnt,sum,root; int head[MAX],low[MAX],dfn[MAX],cpoint[MAX],cedge[MAX];struct edg{int to,next,from; }edge[MAX];void add(int x,int y){edge[++cnt].next=head[x];edge[cnt].from=x,edge[cnt].to=y;head[x]=cnt; }void cutpoint(int u){int fl=0;dfn[u]=low[u]=++cnt;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){cutpoint(v);low[u]=min(low[u],low[v]);if(u!=root&&low[v]>=dfn[u]&&!cpoint[u]) sum++,cpoint[u]=1;if(u==root) fl++;}low[u]=min(low[u],dfn[v]); }if(fl>=2&&!cpoint[u]) sum++,cpoint[u]=1; }void cutedge(int u,int f){dfn[u]=low[u]=++cnt;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){cutedge(v,u);low[u]=min(low[u],low[v]);if(dfn[u]<low[v]) cedge[++sum]=i;}else if(v!=f) low[u]=min(low[u],dfn[v]);} }void mset(){memset(dfn,0,sizeof dfn);memset(low,0,sizeof low);cnt=sum=0; }void find_cutpoint(){for(int i=1;i<=n;i++) if(!dfn[i]) {root=i;cutpoint(i);}printf("%d\n",sum);for(int i=1;i<=n;i++) if(cpoint[i]) printf("%d ",i); }void find_cutedge(){for(int i=1;i<=n;i++) if(!dfn[i]) cutedge(i,0);printf("%d\n",sum);for(int i=1;i<=sum;i++) printf("%d %d\n",edge[cedge[i]].from,edge[cedge[i]].to); }int main(){ // freopen("testdata.txt","r",stdin);scanf("%d %d",&n,&m);for(int i=1;i<=m;i++){int a,b;scanf("%d %d",&a,&b);add(a,b);add(b,a);}find_cutedge();mset();find_cutpoint();return 0; }

轉(zhuǎn)載于:https://www.cnblogs.com/Menteur-Hxy/p/9248051.html

總結(jié)

以上是生活随笔為你收集整理的Tarjan算法 (强联通分量 割点 割边)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。