vijos 观光旅游 最小环fl 呆详看

背景

湖南师大附中成为百年名校之后,每年要接待大批的游客前来参观。学校认为大力发展旅游业,可以带来一笔可观的收入。

描述

学校里面有N个景点。两个景点之间可能直接有道路相连,用Dist[I,J]表示它的长度;否则它们之间没有直接的道路相连。这里所说的道路是没有规定方向的,也就是说,如果从I到J有直接的道路,那么从J到I也有,并且长度与之相等。学校规定:每个游客的旅游线路只能是一个回路(好霸道的规定)。也就是说,游客可以任取一个景点出发,依次经过若干个景点,最终回到起点。一天,Xiaomengxian决定到湖南师大附中旅游。由于他实在已经很累了,于是他决定尽量少走一些路。于是他想请你——一个优秀的程序员——帮他求出最优的路线。怎么样,不是很难吧?(摘自《郁闷的出纳员》)

格式

输入格式

对于每组数据:
第一行有两个正整数N,M,分别表示学校的景点个数和有多少对景点之间直接有边相连。(N<=100,M<=10000)
以下M行,每行三个正整数,分别表示一条道路的两端的编号,以及这条道路的长度。

输出格式

对于每组数据,输出一行:
如果该回路存在,则输出一个正整数,表示该回路的总长度;否则输出“No solution.”(不要输出引号)

样例1

样例输入1

5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
4 3
1 2 10
1 3 20
1 4 30

样例输出1

61
No solution.

限制

各个测试点1s

 

by  http://wenku.baidu.com/view/c6382a41a8956bec0975e3c0.html?from=related
      http://blog.csdn.net/youngyangyang04/article/details/7054931

典型的最小环问题
因为n很小可以直接Floyd求最小环
注意最小环ans更新的位置
每次枚举k为下家更新点
应该先尝试更新ans再更新i,j之间的最短路
我们用g[i][j]来记录i,j之间最短路径
而w[i][j]用来保存i,j之间的原始路径长度
因为我们知道i,j要构成一个最小环
肯定要有两条路可走
1.直接从i到j。
2.从i经过某个中途点k到达j
即对于每一个k 我们先尝试从这个k点对于所有的i,j点能不能得到最小环
然后我们再用这个k点尝试更新路径
该算法的证明:
一个环中的最大结点为K(编号最大),与其相连的两个点为i,j ,
这个环的最短长度为: g[i][k] + g[k][j] + i到j的路径中所有结点编号都不大于k的最短路径长度,
根据floyd的原理,在最外层循环做一个k-1次之后,
g[i][j] 则代表了i到j的路径中所有结点的编号都不大于k的最短路径。
所以我们枚举的i,j要有
1<=i<k,i<j<k
再换一种说法吧
比普通Floyd多出来的部分,主要利用到的原理是当处理到k时,
所有以1 到k - 1为中间结点的最短路径都已经确定,
则这时候的环为(i到j(1 < i, j <= k - 1)的最短路径) + 边(i, k) + 边(k, j)
遍历所有的i, j找到上述式子的最小值即位k下的最小代价环  
*/
/*
路径的求法:
用一个pre[i][j]记录j前面的一个顶点, 初始化为i,当出现需要更新的时候则将pre[i][j] = pre[k][j];
若i== j的时候则表示找全了路径,最后将k点加入路径中
核心代码:
While(i != j)
{  
    if (i != j)
    {  
        Path[len++] = j;
        J = pre[i][j];
    }  
     Path[len++] = i;
}

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int INF=1e8;
int w[102][102],g[102][102];
int n,e;

void init()
{
    int x,y,v;
    for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                w[i][j]=g[i][j]=INF;
    for(int i=1;i<=e;i++)
    {
        scanf("%d%d%d",&x,&y,&v);
        w[x][y]=w[y][x]=g[x][y]=g[y][x]=v;
    }
}

void work()
{
    int ans=INF;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<k;i++)//最小环
            for(int j=i+1;j<k;j++)//为了避免一条边计算两次并且i == j时因为权重会出错 
                ans=min(ans,g[i][j]+w[i][k]+w[k][j]);
        for(int i=1;i<=n;i++)//Floyd
            for(int j=1;j<=n;j++)
                if(i!=j)
                    g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
    }
    if(ans==INF)
        printf("No solution.\n");
    else
        printf("%d\n",ans);
}

int main()
{
    while(scanf("%d%d",&n,&e)!=EOF)
    {
        init();
        work();
    }
}

 

posted @ 2017-07-05 20:17  ioioioioioio  阅读(167)  评论(0编辑  收藏  举报