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;
}