最小環問題

最小環是指在一個圖中,有n個節點構成的邊權和最小的環(n>=3)。

一般來說,最小環分為有向圖最小環和無向圖最小環。

基本介紹

定義,套用,解決方法,樸素算法,傳統的解決方法:dijkstra,floyd求最小環,例題講解,Description,Input,Output,Sample Input,Sample Output,Hint,分析:,標程,

定義

從一個點出發,經過一條簡單路徑回到起點成為.圖的最小環就是所有環中長度最小的。

套用

最小環問題一般出現在計算機科學領域,主要是解決競賽中的一些有關最小環的題目。

解決方法

樸素算法

令e(u,v)表示u和v之間的連邊,再令min(u,v)表示,刪除u和v之間的連邊之後,u和v之間的最短路
最小環則是:min(u,v) + e(u,v)
時間複雜度是EV

傳統的解決方法:dijkstra

任意一個環的權值,我們都可以看成兩個有邊相連的結點i、j的直接距離加上i、j間不包含邊(邊i->j)的最短路徑。求最短路徑我們第一個想到的就是Dijkstra算法。而Dijkstra所求的是一個點到所有點的最短距離。用Dijkstra所求的i、j的最短距離一定是i、j的直接距離(如果i,j連通),所以我們需要先將i、j的邊從圖中刪除(若i,j不連通,則不用刪除),再用Dijkstra求新圖中i、j的最短距離即可。所以我們每次在圖中選取一條邊,把它從圖中刪掉.然後對刪掉的那條邊所對應的2點進行Dijkstra,也就是m次Dijkstra

floyd求最小環

在Floyd的同時,順便算出最小環。
Floyd算法保證了最外層循環到 k 時所有頂點間已求得以 0...k-1 為中間點的最短路徑。一
個環至少有 3 個頂點,設某環編號最大的頂點為 L ,在環中直接與之相連的兩個頂點編號
分別為 M 和 N (M,N < L),則最大編號為 L 的最小環長度即為 Graph(M,L) + Graph(N,L) +
Dist(M,N) ,其中 Dist(M,N) 表示以 0...L-1 號頂點為中間點時的最短路徑,剛好符合 Floyd
算法最外層循環到 k=L 時的情況,則此時對 M 和 N 循環所有編號小於 L 的頂點組合即
可找到最大編號為 L 的最小環。再經過最外層 k 的循環,即可找到整個圖的最小環。
int mincircle = infinity;
Dist = Graph;
for(int k=0;k<nVertex;++k){
//新增部分:
for(int i=0;i<k;++i)
for(int j=0;j<i;++j)
mincircle = min(mincircle,Dist[i][j]+Graph[j][k]+Graph[k][i]);
//通常的 floyd 部分:
for(int i=0;i<nVertex;++i)
for(int j=0;j<i;++j){
int temp = Dist[i][k] + Disk[k][j];
if(temp < Dist[i][j])
Dist[i][j] = Dist[j][i] = temp;
}
}
上面是對無向圖的情況
若是有向圖,只需稍作改動。注意考慮有向圖中 2 頂點即可組成環的情況

例題講解

Description

杭州有 N 個景區, 景區之間有一些雙向的路來連線, 現在 8600 想找一條旅遊路線, 這個路線從 A 點出發並且最後回到 A 點。
假設經過的路線為 V1,V2,....VK,V1,那么必須滿足 K>2,就是說至除了出發點以外至少要經過 2 個其他不同的景區, 而且不能重複經過同一個景區。 現在 8600 需要你幫他找一條這樣的路線, 並且花費越少越好。

Input

第一行是 2 個整數 N 和 M( N <= 800, M <= 600000), 代表景區的個數和道路的條數。
接下來的 M 行里, 每行包括 3 個整數 a,b,c.代表 a 和 b 之間有一條通路, 並且需要花費c 元(c <= 10000)。

Output

如果能找到這樣一條路線的話, 輸出花費的最小值。 如果找不到的話, 輸出 "It'simpossible." (沒有雙引號)

Sample Input

輸入樣例1:
3 3
1 2 1
2 3 1
1 3 1
輸入樣例2:
3 3
1 2 1
1 2 3
2 3 1

Sample Output

輸出樣例1:
3
輸出樣例2:
It's impossible.

Hint

對於 40%的數據: 2 <= n < 100, m<=10000
對於 100%的數據: n <= 800, m<=600000

分析:

加入有一個環,其編號最大的點為 L,那么這個環可以看為 L 與其相鄰的兩個點 A 和
B 與 A 到 B 的最短路上的點(編號均小於 L 的最短路)。
考慮 floyd 算法,由於該算法每次都是求出了 1 到 k−1 做為中間點的最短路然後來求已 k 為
中間點的最短路,則我們可以將其拓展到求最小環

標程

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=810;
const int INF=214748364;
int gi[MAXN][MAXN];
int map[MAXN][MAXN];
int n,m;
int ans=INF;
int main()
{
  int a,b,c;
  scanf("%d%d",&n,&m);
  for(int i=0;i<=n;i++)
    for(int j=0;j<=n;j++)
      {
        if( i==j )
          gi[ i ][ j ]=map[ i ][ j ]=0;
        else
          gi[ i ][ j ]=map[ i ][ j ]=INF;
      }
  for(int i=1;i<=m;i++)
    {
      scanf("%d%d%d",&a,&b,&c);
      if( map[ a ][ b ]>c )
        gi[ a ][ b ]=map[ a ][ b ]=gi[ b ][ a ]=map[ b ][ a ]=c;
    }
  for(int k=1;k<=n;k++)
    {
      for(int i=1;i<=k-1;i++)
        for(int j=i+1;j<=k-1;j++)
          ans=min( ans,gi[ i ][ k ]+gi[ k ][ j ]+map[ i ][ j ]);
      for(int i=1;i<=n;i++)
        for(int j=1;j<=i-1;j++)
          if( j!=k && i!=k )
            map[ j ][ i ]=map[ i ][ j ]=min( map[ i ][ k ]+map[ k ][ j ],map[ i ][ j ]);
    }
  if( ans>=INF )
    printf("It's impossible.\n");
  else
    printf("%d\n",ans);
  return 0;
}

相關詞條

熱門詞條

聯絡我們