1. 程式人生 > >vijos 觀光旅遊 最小環fl 呆詳看

vijos 觀光旅遊 最小環fl 呆詳看

view logs scan 最優 根據 ted 算法 以及 too

背景

湖南師大附中成為百年名校之後,每年要接待大批的遊客前來參觀。學校認為大力發展旅遊業,可以帶來一筆可觀的收入。

描述

學校裏面有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();
    }
}

vijos 觀光旅遊 最小環fl 呆詳看