強連通分量入門——Trajan算法

今天學習了強連通分量。
【參考博客】
如果覺得我講的有些地方難以理解或者存在問題(歡迎留言),可以看一下我借鑒的一些大佬的博客:
傳送門1 傳送門2
【知識儲備】
首先我們需要對幾個定義有一些概念:
強連通:有向圖中兩個點可以相互到達
強連通圖:有向圖中任意兩個點都是強連通的
強連通分量:一個有向圖的一個子圖中是強連通圖的最大的子圖就稱這個有向圖為強連通分量

通俗理解的話,強連通分量里面的任何兩個點都可以相互到達,也就是說至少存在一條路徑可以訪問到所有的點再回到起點(可以經過重復的點),就好像一個環,因此我們用DFS配合專門的算法來解決求連通分量的問題。

【算法介紹】
我們一般用Trajan算法解決相關問題。
正如上面的簡單分析,所謂的強連通分量,就是我們想要找一條可以回到起點的經過盡可能多點的路徑,那如何判斷我們是回到起點(或者已經訪問過的點)呢?我們就需要用數組進行標記每個點的狀態來方便我們進行判斷。
這里引入兩個關鍵的數組:
DFN[MAXN]DFN[ MAXN ]DFN[MAXN] 用來標記DFS訪問到該點時的次序/時間
LOW[MAXN]LOW[MAXN]LOW[MAXN] 用來儲存子樹(從這一點可以訪問到的點)中訪問時間最早的,初始化為DFN,也就是自身的訪問時間。如果訪問到了之前的點,就會變成前面的點的時間戳。

這樣對于之前的點來說就形成了一條從自身出發又回到自身的路徑,也就是一個強連通分量。

【算法實現】

對于每個點我們進行深搜,對每個點打上時間戳(給DFN和LOW賦值)

然后對每一個沒有訪問過的點直接進行 訪問,并且將LOW的值改為所有后面訪問點中最小的。
如果遇到一個訪問過的點

如果他不在棧中,就說明他和這個強連通分量沒有任何關系(在其他地方已經訪問結束,無法到這個點,無法形成回路,否則這個點肯定在棧中)。這里著重需要理解棧中保存的是已經訪問過的點中可以訪問到這個點的點(其他無關的點都已經彈出)

如果這個已經訪問的點在棧中(就比較開心,說明形成環了),如果這個點的最小的時間戳小于他就保存一下LOW,然后這個值就會回溯回去(有可能訪問的這個點在另一個小的強連通分量中,所以我覺得應該比較LOW,但是我看其他人的博客都是比較DFN,仔細想了一下覺得也可以,保存DFN的話也可以,但是我還是覺得比較LOW的話LOW的值就可以代表是否存在在同一個強連通分量中,更加優雅一些 。emm,如果覺得不能理解可以先往下看,不要在在意這些細節 )。

最后DFS結束以后再回來看LOW的值是否改變,如果沒有改變說明這后面的所有點構成一個強連通分量,然后全部彈出(和他沒有關系的早早彈出去了,所以不用擔心后面的沒有關系的點怎么辦)

如果對一個點進行DFS改變LOW的值后LOW的值還沒有改變,仍然等于DFN,說明從這一個點出發是無法回到更早的點的,最早也就是自身,也就是說,他一定不是之前點的連通分量里面的(否則通過它肯定能夠訪問到之前的點,而之前的點的LOW都比較小),所以這個LOW沒有改變的點就是一個連通分量的根節點(比較慘的話就只有他一個節點,但也有可能他的子節點會訪問到他形成連通分量,但無論如何他都是根節點),我們不妨用一個棧保存訪問的順序,那么他后面訪問的點肯定都是他的連通分量中的點,全部彈出即可。(如果不是的話,后面的點肯定自成強連通分量,那么肯定更早彈出了)。 可能稍微有些懵,先有個概念然后再看代碼(注意是遞歸處理的,也就是訪問到后面處理完了再返回來處理前面)。

看著代碼理解一下吧,覺得哪里不能理解可以再看看上面的分析

void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx;	//賦給時間戳stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}

【樣例題目】
Popular Cows

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

* Line 1: Two space-separated integers, N and M* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

*Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

題目大意:

有一群牛,他們相互崇拜,找出所有牛都崇拜的牛有多少

輸入:牛的個數n 崇拜的關系m,然后m行每行A,B,表示A崇拜B

輸出:被所有牛崇拜的牛的個數

【樣例分析】
將崇拜的關系變成一個有向圖,處于一個強連通分量里面的牛肯定是相互崇拜的,我們將一個強連通分量里面的所有牛看成一個點(染色),不同點重新形成一個圖,這個圖里面唯一的出度為0的點中牛的個數就是答案。
因為出度為0的點肯定是被其他點里的牛崇拜的,如果出度為0的點大于1的話兩個出度為0的點里面的牛是沒有辦法崇拜的,所以肯定不能被所有的牛崇拜,所以如果出度為0的點不止一個答案就是0,否則就是出度為0 的點里面牛的個數(出度為0的點肯定是大于等于1的,如果沒有出度為0 的點那么他們形成了一個環,而我們剛才說這是不同強連通分量,如果只有一個連通分量就只有一個點,也符合一個出度為0的點)

【AC代碼】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int MAXN=1e4+5;
const int MAXM=5e4+5;
struct node
{int v,next;
}Edge[MAXM];
int head[MAXN],tot;
int DFN[MAXN],LOW[MAXN];
int color[MAXN],cnt;
bool vis[MAXN];
int idx;
int stack[MAXN],top;
int OutDegree[MAXN];
int n,m;void init()
{memset(head,0,sizeof(head)); tot=0;idx=0; memset(vis,0,sizeof(vis));memset(DFN,0,sizeof(DFN));memset(color,0,sizeof(color));cnt=0; top=0;memset(OutDegree,0,sizeof(OutDegree));
}void read()
{int u,v;for(int i=0;i<m;i++){scanf("%d%d",&u,&v);tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;}
}void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx;stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}void solve()
{int v,mark,num,ans;for(int i=1;i<=n;i++){if(!DFN[i])Trajan(i);}for(int i=1;i<=n;i++){for(int j=head[i];j;j=Edge[j].next){v=Edge[j].v;if(color[i]!=color[v])OutDegree[color[i]]++;}}num=0; mark=-1;for(int i=1;i<=cnt;i++){if(!OutDegree[i]){num++; mark=i;}}ans=0;if(num!=1){printf("0\n");}else{for(int i=1;i<=n;i++){if(color[i]==mark){ans++;}}printf("%d\n",ans);}
}int main()
{while(~scanf("%d%d",&n,&m)){init();read();solve();}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384066.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384066.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384066.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

最小棧的實現

所謂最小棧, 就是當前棧頂元素最小, 我們可以這樣做, 每次在入棧之前, 將待入棧元素與棧頂元素相比, 每次現將待入棧的元素先入棧, 再將帶入棧的元素和較小的元素入棧, 這樣就可以保證每次棧頂元素是最小元素. 在出棧的時候規定每次出棧兩個元素,這樣就可以保證在出棧之后棧頂元…

用C++實現單鏈表的創建、逆置和輸出 的兩種方法

http://blog.csdn.net/lfeng_coding/article/details/47300563 題目描述&#xff1a;在已知單鏈表頭節點的情況下&#xff0c;設計算法逆置單鏈表并輸出 方法一&#xff1a;采用首先將頭節點指向空&#xff0c;讓其變為尾節點&#xff0c;然后利用中間節點 p、q 將其后的節點一…

兩個棧實現一個隊列

利用兩個棧實現一個隊列思路是這樣的. 首先這個隊列包含兩個棧, 然后一個棧用來入隊列, 一個棧用來出隊列 typedef struct QueBy2Stack {SeqStack input;SeqStack output; }QueBy2Stack; 1. 初始化 void QueBy2StackInit(QueBy2Stack* stack) {if(stack NULL){return;//非法…

HDU 5934:Boom——強連通分量+縮點

【題目描述】 There are N bombs needing exploding.Each bomb has three attributes: exploding radius ri, position (xi,yi) and lighting-cost ci which means you need to pay ci cost making it explode.If a un-lighting bomb is in or on the border the exploding ar…

Linux--線程死鎖

http://blog.csdn.net/gebushuaidanhenhuai/article/details/73799824 線程為什會死鎖&#xff1f;&#xff1f;“鎖”又是什么東西&#xff1f;我們這篇博客主要講一下為什么要給線程加鎖&#xff0c;為什么會出現線程死鎖&#xff0c;線程死鎖怎么解決。 互斥鎖 在我的上篇博…

兩個隊列實現一個棧

用兩個隊列實現一個棧的原理是這樣的. 規定兩個隊列, 必須有一個隊列是非空, 一個隊列是空.每次入棧時必須往非空隊列中入, 而每次出棧時, 必須將非空隊列里的元素裝到空隊列中, 直到非空隊列中只有一個元素時, 此時就將剩下的這個元素出棧即可. 而取棧頂元素時, 和出棧一樣, 先…

POJ-1144 Network——Trajan+割點

【題目描述】 A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N . No two places have the same number. The lines are bidirectional and always connect together tw…

Linux--生產者與消費者

http://blog.csdn.net/gebushuaidanhenhuai/article/details/74011636 基本概念 提到生產者和消費者&#xff0c;我們最有可能想到的是商店賣東西&#xff0c;顧客在貨架上(緩沖區&#xff09;買東西。 生產者消費者問題&#xff0c;其實是一個多線程同步問題的經典案例。該問…

進程的掛起以及可重入函數

相關接口 ????pause 函數用于將進程掛起. 如果信號的處理動作是終止進程, 則進程終止, pause 函數沒有返回值; 如果信號的處理動作是忽略, 則進程被掛起, pause函數不返回, 如果信號的處理動作是捕捉, 則調用信號處理動作之后pause 返回 -1.來看一段代碼 #include<s…

POJ1236Network of Schools——強連通分量縮點建圖

【題目描述】 A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distri…

C——通過調用函數分配內存

http://blog.csdn.net/u012627502/article/details/3579724 1&#xff09;以返回值方式返回&#xff1a;把動態分配的存儲位置地址&#xff0c;賦值給指針類型返回值&#xff08;不同于被調用函數的自動變量地址&#xff09; 2&#xff09;以形參形式返回&#xff1a;二級指針類…

gdb調試多進程程序

1.gdb下調試多進程程序只需要以下幾條命令即可 ???????? ????除此之外還可以查看正在調試的進程 info inferiors, 同時也可以將當前正在調試的進程切換到另外一個進程中讓其取運行 ????2.代碼調試演示 #include<stdio.h> #include<stdlib.h> #…

BZOJ1123-BLO——強連通分量求割點+計數

【題目描述】 Byteotia城市有n個 towns m條雙向roads. 每條 road 連接 兩個不同的 towns ,沒有重復的road. 所有towns連通。Input 輸入n<100000 m<500000及m條邊Output 輸出n個數&#xff0c;代表如果把第i個點去掉&#xff0c;將有多少對點不能互通。Sample Input 5…

關于memcpy和memmove兩函數的區別

http://blog.csdn.net/caowei840701/article/details/8491836 [cpp] view plaincopy <p> 關于memcpy和memmove兩個c標準庫函數&#xff0c;其功能都是將一塊內存區域中的指定大小內容復制到目標內存中&#xff0c;在翻閱c標準庫實現的源代碼我們發現他們是有區別的。&…

判斷字符串出棧合法性

先來看說一下思路 接下來就是寫代碼了 int StackOrder(SeqStack* stack, char* input, char* output, int size_input, int size_output) {if(stack NULL || input NULL || output NULL){return 0;}int i_input 0;int j_output 0;SeqStackType value;for(; j_output <…

CodeForces - 1200C——小模擬

【題目描述】 Amugae is in a very large round corridor. The corridor consists of two areas. The inner area is equally divided by n sectors, and the outer area is equally divided by m sectors. A wall exists between each pair of sectors of same area (inner o…

1 單例模式

達內 閔大神 //餓漢單例模式 #include <iostream> using namespace std;class Singleton { public:static Singleton& getInstance(){return s_instance;} private:Singleton(){}Singleton(const Singleton& that){}static Singleton s_instance;//靜態成員變量 …

共享棧

1.定義 所謂共享棧就是利用一個數組實現兩個棧. 先來看一下共享棧的數據結構 typedef struct SharedStack {int top1;int top2;SeqStackType* data; }SharedStack; 2. 初始化 void SharedStackInit(SharedStack* stack) {if(stack NULL){return;//非法輸入}stack -> top…

BZOJ1018 | SHOI2008-堵塞的交通traffic——線段樹維護區間連通性+細節

【題目描述】 BZOJ1018 | SHOI2008-堵塞的交通traffic 有一天&#xff0c;由于某種穿越現象作用&#xff0c;你來到了傳說中的小人國。小人國的布局非常奇特&#xff0c;整個國家的交通系統可 以被看成是一個2行C列的矩形網格&#xff0c;網格上的每個點代表一個城市&#xff0…

C++ 函數隱藏

C該函數隱藏 只有基類成員函數的定義已聲明virtualkeyword&#xff0c;當在派生類中的時間&#xff0c;以支付功能實現&#xff0c;virtualkeyword可以從時間被添加以增加。它不影響多狀態。 easy混淆視聽&#xff0c;掩蓋&#xff1a; &#xff0c;規則例如以下&#xff1a; …