【AcWing 344】 最小環問題 floyd

給定一張無向圖,求圖中一個至少包含3個點的環,環上的節點不重複,並且環上的邊的長度之和最小。

該問題稱爲無向圖的最小環問題。

你需要輸出最小環的方案,若最小環不唯一,輸出任意一個均可。

輸入格式
第一行包含兩個整數N和M,表示無向圖有N個點,M條邊。

接下來M行,每行包含三個整數u,v,l,表示點u和點v之間有一條邊,邊長爲l。

輸出格式
輸出佔一行,包含最小環的所有節點(按順序輸出),如果不存在則輸出’No solution.’。

數據範圍
1≤N≤100,
1≤M≤10000,
1≤l<500
輸入樣例:
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
輸出樣例:
1 3 5 2

代碼和思路來自大佬@spnooyseed的題解
https://www.acwing.com/solution/acwing/content/4494/

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 410 ;
const int INF = 0x3f3f3f3f ;
typedef long long ll ;
int a[N][N] , dis[N][N] ; // a 儲存 兩點之間的距離   dis 儲存兩點之間的最短路徑
int edge[N] ;  // edge 存儲最小環的路徑
int ne[N][N] ; // ne[i][j] 表示從i點到j點這個總的路徑上面(很多條邊) , i 點的下一個點是什麼
int main()
{
  //  freopen("DATA.txt","r",stdin);
    int n , m ;
    scanf("%d%d" , &n , &m) ;
    memset(a , 0x3f , sizeof a) ;
    memset(dis , 0x3f , sizeof dis) ;
    while(m --)
    {
        int x , y ,z ;
        scanf("%d%d%d", &x , &y , &z) ;
        a[x][y] = a[y][x] = dis[x][y] = dis[y][x] = min(dis[x][y] , z) ; // 這個就是怕有重邊的,先篩選一下
        ne[x][y] = y , ne[y][x] = x ;
        // 剛開始以爲x和y之間肯定只有一條邊,然後x——y路徑 x的下一個點就是y ,y的下一個點就是x
    }
    ll ans = INF ;
    int cnt ;
    //下面的大體框架就是floyd算法, 三個循環,
    for(int k = 1 ;k <= n ;k ++)
     {
         for(int i = 1 ;i <= k-1 ;i ++)
          for(int j = i + 1 ;j <= k-1 ;j ++)
           if(ans >0ll +  dis[i][j] + a[i][k] + a[k][j])  // 這個地方就是判斷最小環的地方,如果存在更小的環就處理一下
             {
                 ans =0ll +  dis[i][j] + a[i][k] + a[k][j] ;
                 cnt = 0 ;
                 for(int p = i ;p != j ;p = ne[p][j]) // 這個地方就是將i j 兩點 組成的很多路徑 和最後的一個點k插入edge數組
                 // p = ne[p][j] ,這個的意思就是p-j路徑p的下一個點 ,p從i開始
                  edge[++ cnt] = p ;
                 edge[++ cnt] = j , edge[++ cnt] = k ; // 因爲 k肯定不在i-j這個最短路徑上面,然後單獨將其加入
                 // 因爲上面的一個循環到p == j 就停止了,也就是說j這個點並沒有被插入,所以也突然插一下
             }
         for(int i = 1 ;i <= n ;i ++)
          for(int j = 1 ;j <= n ;j ++)
           if(dis[i][j] > dis[i][k] + dis[k][j])  // 這個就是floyd主算法
            {
                dis[i][j] = dis[i][k] + dis[k][j] ;
                ne[i][j] = ne[i][k] ; // 額外加了一個這個 ,因爲i--j 已經是最短了
               // ,此時此刻i-j路徑上面只有三個點,因爲是從ijk三個點轉移過來的 , 所以 i 的下一個點肯定是k
            }
     }
     // 最後就處理一下就行了

    if(ans == INF )
     cout << "No solution." << endl ;
    else
    {
        for(int i = 1 ;i <= cnt ;i ++)
         cout << edge[i] << " " ;
        cout << endl ;
    }
    return 0 ;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章