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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法提高课-图论-有向图的强连通分量-AcWing 1174. 受欢迎的牛:tarjan算法求强连通分量、tarjan算法板子、强连通图

發布時間:2025/4/5 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法提高课-图论-有向图的强连通分量-AcWing 1174. 受欢迎的牛:tarjan算法求强连通分量、tarjan算法板子、强连通图 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • 題目解答
      • 題目來源

題目解答


來源:acwing

分析:

強連通圖:給定一張有向圖。若對于圖中任意兩個結點x,y,既存在從x到y的路徑,也存在從y到x的路徑,則稱該有向圖是“強連通圖”。

強連通分量(SCC)指的是強連通圖的極大連通子圖(點最多的)。

說人話,強連通分量中任意兩點都存在邊再加入任何別的點,它都不再是連通分量。

或者說,對于一張有向圖,強連通分量就是任意兩點都有路徑(x能到y, y也能到x),并且包含點最多的圖。

有向圖的強連通分量有什么用呢?

主要是通過縮點(將強連通分量縮成一個點),把有向圖,轉換為有向無環圖(拓撲圖,DAG)。

如下圖,左圖圈內的是一個強連通分量,通過縮點,轉化為右圖。這種做法其實有很多應用,比如求最短路等。

需要知道的幾個概念:

  • 樹枝邊(x,y)
    x是y的父節點,x到y的邊稱為樹枝邊
  • 前向邊(x,y)
    x是y的祖先節點,x到y的邊稱為前向邊。
    樹枝邊是特殊的前向邊。
  • 后向邊(y,x)
    x是y的祖先節點,y到x的邊稱為后向邊。
  • 橫叉邊(x,y)
    在對有向圖進行dfs遍歷時,x是已經搜過的圖的分支(不是前向邊),現在在搜的點是y,y到x的有向邊是橫叉邊。
  • tarjin算法求強連通分量

    引入時間戳的概念:在dfs遍歷的過程中,按照每個結點第一次被訪問的時間順序,依次給予圖中N個結點1~N的整數標記,該標記被稱為時間戳,記為dfn[x].

    對每個點定義兩個時間戳:
    dfn[u]表示遍歷到u的時間戳;

    low[u] 表示從u開始走(遍歷它的子樹),所能遍歷到的最小時間戳是什么。

    我們在求強連通分量的時候,求的是每個強連通分量最上面的那個點。

    u是所在的強連通分量的最高點,等價于dfn[u]== low[u],因為low[u]表示的是從u開始能夠遍歷到的最小的時間戳,正好等于自己的時間戳,說明什么? 說明u就是這個連通分量的最高點啊!

    tarjan算法求強連通分量的模板

    背模板的思路

    /* 1. 加時間戳; 2. 放入棧中,做好標記; 3. 遍歷鄰點1)如果沒遍歷過,tarjan一遍,用low[j]更新最小值low2) 如果在棧中,用dfn[j]更新最小值low 4.找到最高點1)scc個數++2)do-while循環:從棧中取出每個元素;標志為出棧;對元素做好屬于哪個scc;該scc中點的數量++ */

    具體模板代碼

    // tarjan 算法求強連通分量 // 時間復雜度O(n+ m) void tarjan(int u){// 初始化自己的時間戳dfn[u] = low[u] = ++ timestamp;//將該點放入棧中stk[++ top] = u, in_stk[u] = true;// 遍歷和u連通的點for(int i = h[u]; ~i; i = ne[i]){int j = e[i];if(!dfn[j]){tarjan(j);// 更新u所能遍歷到的時間戳的最小值low[u] = min(low[u], low[j]);}// 如果當前點在棧中//注意棧中存的可能是樹中幾個不同分支的點,因為有橫叉邊存在// 棧中存的所有點,是還沒搜完的點,同時都不是強連通分量的最高點// 這里表示當前強連通分量還沒有遍歷完,即棧中有值else if(in_stk[j])//更新一下u點所能到的最小的時間戳//此時j要么是u的祖先,要么是橫叉邊的點,時間戳小于ulow[u] = min(low[u], dfn[j]);}// 找到該連通分量的最高點if(dfn[u] == low[u]){int y;++ scc_cnt; // 強連通分量的個數++do{// 取出來該連通分量的所有點y = stk[top --];in_stk[y] = false;id[y] = scc_cnt; // 標記點屬于哪個連通分量size_scc[scc_cnt] ++;} while(y != u);} }

    對于本題的解析:

    題意是找到被其他所有牛都歡迎的牛的數量。在有向圖的角度,就是所有的點都可以走到當前這個點。

    如果暴力做的話,對于每個點都要dfs或者bfs,看是否所有點都可以到達該點,做一遍的時間復雜度是O(n + m),那么n個點,時間復雜度是O(n(n+m)),這里的n是1w,m是5
    w,時間限制是1s,會超時。

    優化的解法:

    如果是拓撲圖(有向無環圖)的話,就好解決了。為什么呢?只需要統計出度為0的點的數量。如果出度為0的點的數量大于等于2,那么一定不存在最受歡迎的牛(其他所有點都有邊連到該點)。
    這是因為是有向無環圖,如果有2個或以上的點出度為0,那么其中1個葉子結點必然有到不了的點,也就是不存在最受歡迎的牛。這個需要畫圖理解一下。

    如果只存在一個出度為0的點呢?

    綜上,如果是拓撲圖的話,這道題就好做了。實際上,我們可以用強連通分量算法將圖轉換為拓撲圖!

    先求出該圖的強連通分量,然后縮點,變成有向無環圖。找到出度為0的點的數量為1的情況,然后統計該點所表示的強連通分量,其中包含多少個點,這里的所有點都可以被其他所有點走到。

    ac代碼

    #include<bits/stdc++.h> using namespace std;const int N = 10010, M = 50010;int n, m; int h[N], w[M], e[M], ne[M], idx; // 鄰接表一套 // dfn[u] 存的是遍歷到u的時間戳 // low[u]存的是從u出發,遍歷子樹,所能遍歷到的最小的時間戳 //timestamp 就是時間戳 int dfn[N], low[N], timestamp; int stk[N], top; // 棧,和棧頂元素索引 bool in_stk[N]; // 是否在棧中 //id[u]表示u的強連通分量的編號,scc_cnt表示強連通分量的編號 // size_scc[u]表示編號為u強連通分量中點的數量 int id[N], scc_cnt, size_scc[N]; int dout[N];// 記錄新圖中每個點(也就是原圖每個連通分量)的出度void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++; }void tarjan(int u){//當前點的時間戳dfn[u] = low[u] = ++ timestamp;// 加入棧中stk[++ top] = u, in_stk[u] = true;//遍歷u點的所有鄰點for(int i = h[u]; ~i; i = ne[i]){int j = e[i];if(!dfn[j]){//如果沒有遍歷過tarjan(j); // 遍歷它low[u] = min(low[u], low[j]);}// 當前點在棧當中else if(in_stk[j]) low[u] = min(low[u], dfn[j]);}if(dfn[u] == low[u]){++ scc_cnt; // 更新強連通分量的編號int y;do{y = stk[ top--]; //不斷取出棧內元素in_stk[y] = false;id[y] = scc_cnt; //y元素所屬的連通塊編號size_scc[scc_cnt] ++; //該連通塊內包含的點數}while(y != u); // 直到y不等于u}}int main(){cin >> n >> m;memset(h, -1, sizeof h);while(m --){int a, b;cin >> a >> b;add(a, b);}for(int i = 1; i <= n; i ++){if(!dfn[i])tarjan(i);}// 建有向無環圖// 統計在新圖中所有點的出度for(int i = 1; i <= n; i ++){for(int j = h[i]; ~j; j = ne[j]){int k = e[j];int a = id[i]; //a表示i所在連通分量的編號int b = id[k]; // b表示k所在連通分量的編號//如果點i和點k不在同一個連通塊// dout存的是出度,因為本題只需要出度//在其他題目中,可能是要建邊,因為這里是構造有向無環圖if(a != b) dout[a] ++; // 從a走到b,a的出度++}}// 和本題有關的部分:// zeros是統計在新圖中,出度為0的點的個數// sum表示滿足條件的點(最受歡迎的奶牛)的個數int zeros = 0, sum = 0;for(int i = 1; i <= scc_cnt; i ++){if(!dout[i]){zeros ++;sum += size_scc[i];if(zeros > 1){sum = 0;break;}}}cout << sum << endl; }

    題目來源

    https://www.acwing.com/problem/content/1176/

    總結

    以上是生活随笔為你收集整理的算法提高课-图论-有向图的强连通分量-AcWing 1174. 受欢迎的牛:tarjan算法求强连通分量、tarjan算法板子、强连通图的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。