給出一張無向圖,求一個最小環並輸出路徑。
說說我的感覺:
包含點 i 和點 j 的最小環,我們可以看成是 i 到 j 之間的最短路和次短路的組合,通過 floyd 可求任意兩點之間的最短距離,那麼我們只要找到最短路徑外的一條最短路來保證 i 和 j 之間可達即可。在做 floyd 迴圈的同時,我們以 環權值 最小(最短路權值+次短路權值=最小環權值)為標準,一直更新每個點的前驅,也就是記錄 i 到 j 的最短路徑,以及,能夠鬆弛 i 和 j 的點 k (k 不在 i 到 j 的最短路徑中)中代價最小的那個(也就是 i 到 j 之間的次短路),然後按環的自然順序輸出即可。
代碼中也注釋的很詳細了:
#include<cstdio>#include<cstring>#define find_min(a,b) a<b?a:bconst int N = 101;const int INF = 0x7ffffff;int mat[N][N],dist[N][N],pre[N][N],path[N],n;int main(){int i,j,k,m,a,b,c;int num;while(~scanf("%d%d",&n,&m)){for(i=1;i<=n;i++){for(j=1;j<=n;j++){mat[i][j]=dist[i][j]=INF;pre[i][j]=i;}}while(m--){scanf("%d%d%d",&a,&b,&c);mat[a][b]=mat[b][a]=dist[a][b]=dist[b][a]=find_min(mat[a][b],c);}int min=INF;for(k=1;k<=n;k++){//最短路徑外一點將最短路首尾連結,那麼就得到一個最小環for(i=1;i<k;i++){for(j=i+1;j<k;j++){//求最小環不能用兩點間最短路鬆弛,因為(i,k)之間的最短路,(k,j)之間的最短路可能有重合的部分//所以mat[][]其實是不更新的,這裡和單純的floyd最短路不一樣//dist[i][j]儲存的是 i 到 j 的最短路權值和int tmp=dist[i][j]+mat[i][k]+mat[k][j];//這裡 k 分別和 i 還有 j 在mat中直接相連if(tmp<min){min=tmp;num=0;int p=j;while(p!=i){//回溯path[num++]=p;p=pre[i][p];//pre[i][j]表示 i 到 j 最短路徑上 j 前面的一個點}path[num++]=i;path[num++]=k;}}}for(i=1;i<=n;i++){for(j=1;j<=n;j++){if(dist[i][j]>dist[i][k]+dist[k][j]){dist[i][j]=dist[i][k]+dist[k][j];//dist[][]儲存兩點間最短距離pre[i][j]=pre[k][j];}}}}if(min==INF)puts("No solution.");else{printf("%d",path[0]);for(i=1;i<num;i++)printf(" %d",path[i]);puts("");}}return 0;}