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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

信息传递

發(fā)布時間:2025/4/9 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 信息传递 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

noip2015d1t2。

原題鏈接:https://www.luogu.org/problem/show?pid=2661

這道題其實是讓你求一個圖中最小環(huán)的長度。正常向方法是搜索+剪枝,不過也有一個叫tarjan的算法,可以用來求強聯(lián)通分量。

兩個方法我都說一下吧。

正常向方法:

1.建圖,這里一般用鄰接表,當然不用也行,因為就這個題來說沒必要記錄這么多信息,只記錄某個點的連接的下一個點的編號就行了,我這里就沒寫正常鄰接表。

2.以每個點為起點進行一次DFS。這里有一步判環(huán)操作:打標記,也就是所謂的時間戳,判環(huán)用的。每次擴展節(jié)點的時候查看一下這個點是不是有時間戳,如果有那就說明這個點曾經(jīng)擴展過,那就是成環(huán)了。把以所有點出發(fā)的環(huán)的長度求出來,取一個最小值就是答案。應該需要隊列。

(我要是沒記錯的話STL隊列會T,所以建議手寫隊列)

我使用了拓撲排序,顯然題中的這個圖一定是個DAG,所以在拓撲排序的時候,我們要不斷地找入邊,這樣可以刪除不成環(huán)的邊,刪完后就剩下一個個的環(huán)。

再在這些環(huán)上用DFS求最小環(huán)長度就好了。

?

第二種方法是用tarjan算法求強聯(lián)通分量。這個算法是由一個名叫tarjan的人提出來的,由他的名字命名 (但這個詞到底怎么讀,我遇到過好多版本的)。。

先說一下什么叫強聯(lián)通分量。如果兩個頂點可以相互通達,則稱兩個頂點強連通。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量。

tarjan算法可以在線性時間復雜度內(nèi)求出一個有向圖的強聯(lián)通分量。

Tarjan算法是基于對圖深度優(yōu)先搜索的算法,每個強連通分量為搜索樹中的一棵子樹。

搜索時,把當前搜索樹中未處理的節(jié)點放到一個棧里面,在回溯的時候就可以判斷棧頂?shù)綏V械墓?jié)點是否為一個強連通分量。

為了方便,我們定義(好多文章中都是以這個名字定義的,我也就沿用了):

dfn[ i ] 為在DFS中該節(jié)點被搜索的次序,也就是時間戳。

low[ i ] : 為i或i的子樹能夠追溯到的最早的棧中節(jié)點的次序號。

可以證明,當dfn[ i ]=low[ i ]時,i或i的子樹可以構(gòu)成一個強連通分量。

  具體演示過程,可以參考這位BYVoid博主寫的博文:https://www.byvoid.com/zhs/blog/scc-tarjan

  這里給出分別使用兩種方法的AC代碼:

  1.正常向:

  

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #define maxn 200005 6 #define INF 1e9+7; 7 using namespace std; 8 int n; 9 int d[maxn],vis[maxn],to[maxn],q[maxn]; 10 /* 11 d數(shù)組記錄某個點的入度 12 vis數(shù)組便是標記數(shù)組 13 to數(shù)組記錄從某個點出發(fā)指向哪里,因為按照題意每個點的目標只有一個 14 q為一般隊列 15 */ 16 int ans=INF; 17 inline int read(){ 18 int num = 0; 19 char c; 20 bool flag = false; 21 while ((c = getchar()) == ' ' || c == '\n' || c == '\r'); 22 if (c == '-') flag = true; 23 else 24 num = c - '0'; 25 while (isdigit(c = getchar())) 26 num = num * 10 + c - '0'; 27 return (flag ? -1 : 1) * num; 28 } 29 void tppx(void){ 30 int head=0; 31 int tail=0; 32 for (int i=1;i<=n;i++)//拓撲排序先標記一下入度為0的點 33 if (d[i] == 0){ 34 vis[i] = 1; 35 q[++tail] = i; 36 } 37 while (head<tail){//從入度為0的點開始BFS 38 int now=q[++head];//取隊首并彈出隊首,簡化寫法,以下同 39 d[to[now]]--;//每次取了一條邊也可看作刪掉了這條邊,入度-1 40 if(d[to[now]]==0){//如果下一個連接的點也入度為0了,那么再以這個點進行BFS(想一下拓撲排序的主過程) 41 vis[to[now]] = 1; 42 q[++tail] = to[now]; 43 } 44 } 45 } 46 void dfs(int now,int k){//以每個點為起點遍歷整個圖,k用來記錄當前的長度,求一個最小的ans就是答案 47 vis[now] = 1; 48 if (vis[to[now]]==0) 49 dfs(to[now],k+1); 50 else 51 ans = min(ans,k); 52 53 } 54 int main(){//主過程就很明顯了吧 55 n = read(); 56 for (int i=1;i<=n;i++){ 57 to[i] = read(); 58 d[to[i]]++; 59 } 60 tppx(); 61 for (int i=1;i<=n;i++){ 62 if (!vis[i]) 63 dfs(i,1); 64 } 65 cout << ans << endl; 66 return 0; 67 }

  

  2.使用tarjan算法:

  

1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #define maxn 200005 6 #define INF 1e9+7; 7 using namespace std; 8 int n; 9 int ans=INF; 10 int s[maxn],top=0; 11 int dfn[maxn],low[maxn],tot=0; 12 int head[maxn],cnt=0; 13 int vis[maxn]; 14 struct Edge{//這里我用了正常鄰接表 15 int from; 16 int to; 17 }; 18 Edge edge[maxn]; 19 inline int read(){//到哪都少不了的快讀 20 int num = 0; 21 char c; 22 bool flag = false; 23 while ((c = getchar()) == ' ' || c == '\n' || c == '\r'); 24 if (c == '-') flag = true; 25 else 26 num = c - '0'; 27 while (isdigit(c = getchar())) 28 num = num * 10 + c - '0'; 29 return (flag ? -1 : 1) * num; 30 } 31 void add_edge(int from,int to){ 32 edge[++cnt].from=head[from]; 33 edge[cnt].to = to; 34 head[from]=cnt; 35 } 36 void tarjan(int u){ 37 vis[u]=1; 38 dfn[u]=low[u]=++tot; 39 s[++top]=u; 40 for(int i=head[u];i!=0;i=edge[i].from){ 41 int v = edge[i].to; 42 if(!dfn[v]){ 43 tarjan(v); 44 low[u]=min(low[u],low[v]); 45 } 46 else if(vis[v]) 47 low[u]=min(low[u],dfn[v]); 48 } 49 if(low[u]==dfn[u]){ 50 int temp=0; 51 do{ 52 vis[s[top]]=0; 53 temp++; 54 }while(s[top--] != u); 55 if(temp!=1) 56 ans=min(ans,temp); 57 } 58 } 59 int main(){ 60 n = read(); 61 for(int u=1;u<=n;u++){ 62 int v; 63 v = read(); 64 add_edge(u,v); 65 } 66 for(int i=1;i<=n;i++)//依然是每個點都要考慮,從每個點出發(fā)求一遍連通分量取最小值 67 if(!dfn[i]) 68 tarjan(i); 69 cout << ans << endl; 70 return 0; 71 }

?

轉(zhuǎn)載于:https://www.cnblogs.com/OIerShawnZhou/p/7451095.html

總結(jié)

以上是生活随笔為你收集整理的信息传递的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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