图论——最小环
圖論——最小環
對于一個有向圖或一個無向圖,如何求它的最小環的大小呢,默認邊的權都是1。
Floyd算法求最小環(性能差)
設節點 i i i和 j j j為遍歷節點 k k k為中點,那么當 i = j i=j i=j的時候,如果存在 i → k i \to k i→k和 k → j k \to j k→j,那么就會存在環 i → k → j ( i ) i \to k \to j(i) i→k→j(i)此時最短路就是環的大小。
#include <bits/stdc++.h>using namespace std;#define FR freopen("in.txt","r",stdin)#define UNC 1000typedef long long ll; int d[100][100]; int n,m;int flody() {int ans = UNC;for(int k = 1; k<=n; k++)for(int i = 1; i<=n; i++)for(int j = 1; j<=n; j++){d[i][j] = min(d[i][j],d[i][k] + d[k][j]);if(i == j)ans = min(ans,d[i][k] + d[k][j]);}return ans; }int main() {cin >> n >> m;for(int i = 1; i<=n; i++)for(int j = 1; j<=n; j++)d[i][j] = UNC;for(int i = 0; i<m; i++){int u,v;cin >> u >> v;d[u][v] = 1;}cout << flody();return 0; }網上還流傳一種做法是在 k k k循環內做兩個內層循環,具體原理應該是把一個環中的節點按照從小到大排序,最小的那個就是 i i i,第二大的那就是 j j j,最大的就是 k k k,因此循環的時候 i < j < k i \lt j \lt k i<j<k,避免了重復計算,但是我不知道為什么要套兩個循環,像我上面這樣不行嗎?
注意有向圖請注意自環。
拓撲排序(不通過)
注意:從入度為0的節點開始消除,剩下的都是環,這句話沒錯,但是留下的圖一定有環,只能說明存在環,不能計算最小環的大小。
Tarjan算法(通過)
在DFS過程中,如果遇到了后向邊,那么就說明遇到了環,利用時間戳計算環的大小,最小大小就是最小環的大小。
P2661
#include <bits/stdc++.h>using namespace std;#define FR freopen("in.txt","r",stdin) #define FW freopen("out1.txt","w",stdout)typedef long long ll;struct Edge {int to;int nxt; } e[200005]; int head[200005]; int d[200005]; bool vis[200005]; int ti = 0; int tot = 0; int n;inline void add(int u,int v) {tot++;e[tot].to = v;e[tot].nxt = head[u];head[u] = tot; }int ans = INT_MAX;void dfs(int curr) {ti++;d[curr] = ti;for(int ne = head[curr]; ne != 0; ne = e[ne].nxt){int v = e[ne].to;if(d[v] != 0){if(!vis[v])ans = min(ans,d[curr] - d[v] + 1);}else{dfs(v);}}vis[curr] = true; }int main() {cin >> n;for(int i = 1; i<=n; i++){int v;cin >> v;add(i,v);}for(int i = 1; i<=n; i++){if(!vis[i]) dfs(i);}cout << ans;return 0; }并查集(通過)
代碼取自anyway。只針對于本題,本題沒有環套環,因此b一定是根節點。
#include<cstdio> #include<iostream> using namespace std; int f[200002],d[200002],n,minn,last; //f保存祖先節點,d保存到其祖先節點的路徑長。 int fa(int x) {if (f[x]!=x) //查找時沿途更新祖先節點和路徑長。 {int last=f[x]; //記錄父節點(會在遞歸中被更新)。 f[x]=fa(f[x]); //更新祖先節點。 d[x]+=d[last]; //更新路徑長(原來連在父節點上)。 }return f[x]; } void check(int a,int b) {int x=fa(a),y=fa(b); //查找祖先節點。 if (x!=y) {f[x]=y; d[a]=d[b]+1;} //若不相連,則連接兩點,更新父節點和路徑長。 else minn=min(minn,d[a]+d[b]+1); //若已連接,則更新最小環長度。 return; } int main() {int i,t;scanf("%d",&n);for (i=1;i<=n;i++) f[i]=i; //祖先節點初始化為自己,路徑長為0。 minn=0x7777777;for (i=1;i<=n;i++){scanf("%d",&t);check(i,t); //檢查當前兩點是否已有邊相連接。 }printf("%d",minn);return 0; }總結
- 上一篇: 科技战疫志愿精神如何延续?腾讯的答案是…
- 下一篇: 华为matebook14摄像头无法启动问