http://poj.org/problem?id=1734
题意:
给定一张图,n个点m条无向边(存在重边)。求该图的最小环(边权和最小) 注意此环满足展开的路径v1,v2,v3...v1中出了v1之外其他的点都必须不同,业绩不会存在1 2 3 3 2 1这样的环
思路:
1:朴素的求最小环的方法做E遍Dijkstra,枚举每条边e(i,j),删去边e(i,j)之后,求i到j的最短路经,然后再加上该边求最小值便得到的了最小环,时间复杂度为O(E*(N^2))。
2:改进的floyd算法,求出任意两点之间的最短路的同时,求出最小环。
这里是讲解求最下环的过程:
先说一下Floyd算法和用Floyd算法求最小环,
在说一下隐藏在两个算法中间的图论的一种思想方法
Floyd算法:
d [ i ][ j ] 为 i 到 j 之间的距离
for(int k = 1;k <= n;k ++)
for(int i = 1 ;i <= n; i++)
for(int j = 1;j <= n;j++)
{
if( d[ i ][ j ] > d[ i ][ k ] + d[ k ][ j ] )
d[ i ][ j ] = d[ i ][ k ] + d[ k ][ j ] ;
}
用于求一个图中任意两个点之间的最小距离
Floyd算法求最小环
d[ i ][ j ] 为i 到 j 之间的距离
g[ i ][ j ] 为i 到 j 的边长
初始d[ i ][ j ] = g[ i ][ j ]
for ( k = 1;k <= n; k ++)
{
for( i = 1 ; i <= k-1 ; i ++)
for( j = i + 1; j <= k - 1; j ++ )
ans = min ( ans , d[ i ][ j ] + g[ i ][ k ] + g[ k ][ j ]) ;//这里保证了i->j的最小环里面不会存在重复的点
for( i = 1 ; i <= n ; i ++)
for( j = i ; j <= n; j ++ )
d[ i ] [ j ] = min(d[ i ][ j ],d [ i ][ k ] + d[ k ][ j ]);
}
图论中有个经常用的思想方法:
就是在求某个具有群性质的解的时候,如最小环。
往往是先考虑 在满足这个要求的集合中加入一个点,看是否满足这个要求或者需要做某种维护操作,然后逐渐将这个集合扩大
最终,当所有的点都考虑过的时候,所得的这个集合就是最终 的结果。
下面以最小环为例:
设满足这个最小环的点的集合为C
初始C = 空集
最外层的k循环就是分别考虑图中的每个点,依次将第k个点加入这个集合
for( i = 1 ; i <= k-1 ; i ++)
for( j = i + 1; j <= k - 1; j ++ )
ans = min ( ans , d[ i ][ j ] + g[ i ][ k ] + g[ k ][ j ]) ;
是考虑集合中的任意两点i,j,考率在集合C中加入k,能否构成最小环,并对最小环的长度进行更新
然后
for( i = 1 ; i <= n ; i ++)
for( j = i ; j <= n; j ++ )
d[ i ] [ j ] = min(d[ i ][ j ],d [ i ][ k ] + d[ k ][ j ]);
把第k个点加入集合C中,同时对集合中的其他点的距离进行维护
如刚开始C={1,2,3}
从C中任选两点i,j
k = 4
然后考虑4能否加入C中,同时对最小环的长度进行更新,维护一下集合C的性质,
i到k,k到j和本身C中的i到j构成一个环。
然后再考虑k = 5
吐槽一下:当你的inf设置的很大时,一进行运算就会很容易超数据类型。所以在floyd求解遇到运算时,需要判断一下两点之间的距离,如果为inf说明没有意思:
//#pragma comment(linker,"/STACK:327680000,327680000") #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define inf 0x7f7f7f7f #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x)int #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define N 105 using namespace std; int f[N][N],g[N][N]; int pre[N][N]; int ans[N],len; int n,m; void init() { int i,j; for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { f[i][j] = g[i][j] = inf; pre[i][j] = i;//记录i->j这个最小环在j之前的第一个点 } } } int res; void floyd() { res = inf; int i,j,k; for (k = 1; k <= n; ++k) { for (i = 1; i <= k - 1; ++i) { for (j = i + 1; j <= k - 1; ++j) { //这里忘记判断inf结果导致wa很多次,无语。。。 if (g[j][k] != inf && g[k][i] != inf && res > f[i][j] + g[j][k] + g[k][i]) { res = f[i][j] + g[j][k] + g[k][i]; len = 0; int mk = j; while (mk != i) { ans[len++] = mk; mk = pre[i][mk]; } ans[len++] = i; ans[len++] = k; } } } for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { if (f[i][k] != inf && f[k][j] != inf && f[i][j] > f[i][k] + f[k][j]) { f[i][j] = f[i][k] + f[k][j]; pre[i][j] = pre[k][j];//更新pre[i][j] } } } } } int main() { //Read(); int i; int x,y,z; while (~scanf("%d%d",&n,&m)) { init(); for (i = 1; i <= m; ++i) { scanf("%d%d%d",&x,&y,&z); if (z < f[x][y]) { f[x][y] = f[y][x] = z; g[x][y] = g[y][x] = z; } } floyd(); if (res < inf) { for (i = 0; i < len - 1; ++i) printf("%d ",ans[i]); printf("%d\n",ans[len - 1]); } else { printf("No solution.\n"); } } return 0; }