給定一張無向圖,求圖中一個至少包含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 ;
}