pku 1734 Sightseeing trip floyd求最小环

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中,同时对集合中的其他点的距离进行维护

 

pku 1734 Sightseeing trip floyd求最小环

如刚开始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;

}

  

 

你可能感兴趣的:(floyd)