题意:求有权无向图的最小环,环至少包括三个点。
思路:
设map[i,j]表示i到j的的距离。输入有重边,在处理输入的时候只保存最短边。
取环中一个点k,左右点是ij则map[i,k]和map[k,j]是固定的不能变,可改变的是没有加入k点的i,j之间的最短路,设为dist[i,j]。那么最短环的长度表示为dist[i,j]+map[i,k]+map[k,j]。
Floyd的最外层循环为k时,最短路还没有用k和更新ij之间的最短路,恰好符合要求。所以在还没有用k更新ij之间的最短路之前更新环,每次更新环时更新路径即可。
算法步骤:
输入输出Floyd算法的框架,三个循环,在更新路径前更新环的最短路径。题解一和其他的题解(都是DP)代码很短,步骤很清晰,所以算法步骤粗略写写。
算法复杂度:
输入输出两个循环不超过O(n2),时间主要消耗在Floyd算法的框架里,三个n次循环,所以时间复杂度O(n3),最多是二维数组,空间复杂度是O(n2)
代码:
1 const int maxn=110; 2 int dist[maxn][maxn], map[maxn][maxn]; //最短距离,原图 3 int pre[maxn][maxn]; // pre[i,j]记录最短路里,j前面一个点 4 int path[maxn]; // 答案路径 5 int n, m, num, minc; // num记录path里有多少个点,minc是最短环长度 6 7 int main() 8 { 9 int u, v, cost; 10 while(cin >> n && n){ 11 if(n<0) break; 12 cin >> m; 13 for(int i=1; i<=n; i++){ 14 for(int j=1; j<=n; j++){ 15 dist[i][j]=map[i][j]=INF; 16 pre[i][j]=i; 17 } 18 } 19 for(int i=1; i<=m; i++){ 20 scanf("%d %d %d",&u,&v,&cost); 21 if(dist[u][v]>cost) //重边 22 map[u][v]=map[v][u]=dist[u][v]=dist[v][u]=cost; 23 } 24 // floyd 25 minc=INF; 26 for(int k=1; k<=n; k++){ 27 // k还没加入(i,j)最短路,更新最短环 28 for(int i=1; i<k; i++){ 29 for(int j=i+1; j<k; j++){ 30 int ans=dist[i][j]+map[i][k]+map[k][j]; 31 if(ans<minc){ //找到最优解 32 minc=ans; 33 num=0; 34 int p=j; 35 while(p!=i){ //逆向寻找前驱遍历的路径并将其存储起来 36 path[num++]=p; 37 p=pre[i][p]; 38 } 39 path[num++]=i; 40 path[num++]=k; 41 } 42 } 43 } 44 //用k更新i到j的最短路径 45 for(int i=1; i<=n; i++){ 46 for(int j=1; j<=n; j++){ 47 if(dist[i][j]>dist[i][k]+dist[k][j]){ 48 dist[i][j]=dist[i][k]+dist[k][j]; 49 pre[i][j]=pre[k][j]; 50 } 51 } 52 } 53 }// end Floyd 54 if(minc==INF) puts("No solution."); 55 else{ 56 printf("%d",path[0]); 57 for(int i=1; i<num; i++) 58 printf(" %d",path[i]); 59 puts(""); 60 } 61 } 62 return 0; 63 }