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

 

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

  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值