「LOJ#10072」「一本通 3.2 例 1」Sightseeing Trip(無向圖最小環問題)(Floyd


題目描述

原題來自:CEOI 1999

給定一張無向圖,求圖中一個至少包含 333 個點的環,環上的節點不重復,並且環上的邊的長度之和最小。該問題稱為無向圖的最小環問題。在本題中,你需要輸出最小環的方案,若最小環不唯一,輸出任意一個均可。若無解,輸出 No solution.。圖的節點數不超過 100100100

輸入格式

第一行兩個正整數 n,mn,mn,m 表示點數和邊數。
接下來 mmm 行,每行三個正整數 x,y,zx,y,zx,y,z,表示節點 x,yx,yx,y 之間有一條長度為 zzz 的邊。

輸出格式

輸出一個最小環的方案:按環上順序輸出最小環上的點。若最小環不唯一,輸出任意一個均可。若無解,輸出 No solution.

 

樣例

樣例輸入

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

樣例輸出

1 3 5 2

題解

無向圖的最小環問題啊,不知道在多少場考試中間卡住了我的臨門一腳。——hzz

教材:http://www.cnblogs.com/Yz81128/archive/2012/08/15/2640940.html#undefined

這個最小環中,一定會有一個編號最大的點。

那么我們設$dp[i][j][k]$為在只經過$1$~$k$的點時,$i$,$j$的最短距離。

那么如果我們設最小環中最大的點為$k$,它兩邊有點$i$,$j$,

那么答案的最小值就是$dp[i][j][k-1]+g[j][k]+g[k][i]$。

//

然后我們聯想Floyd的過程,

$for$ $int$ $k=1$ $to$ $n$

    $for$ $int$ $i=1$ $to$ $n$

  $for$ $int$ $j=1$ $to$ $n$

也就是說,在$k$時,$i$,$j$的最小值都只中轉了$1$~$k$的點。

這不就是我們要的$dp[i][j][k]$嘛?!

於是我們可以在Floyd的同時順便維護答案。

並且$dp$數組的$k$那一維還是可以省掉的。

//詳見代碼吧qwq

 1 編號    題目    狀態    分數    總時間    內存    代碼 / 答案文件    提交者    提交時間
 2 #239580    #10072. 「一本通 3.21」Sightseeing Trip Accepted    100    45 ms    380 KiB    C++ / 1.2 K    qwerta    2018-10-22 16:48:53
 3 
 4 #include<iostream>
 5 #include<cstring>
 6 #include<cstdio>
 7 #include<cmath>
 8 using namespace std;
 9 #define LL long long 
10 int g[103][103];//直接連邊的距離
11 int dis[103][103];//有中轉點的距離
12 int q[103];
13 int toq=0;
14 int zz[103][103];//zz[i][j]表示從i到j的最小路徑的中轉點
15 void getpath(int u,int v)
16 {
17     if(!zz[u][v])return;
18     getpath(u,zz[u][v]);//從u往中轉點找
19     q[++toq]=zz[u][v];
20     getpath(zz[u][v],v);
21     return;
22 }
23 int main()
24 {
25     //freopen("a.in","r",stdin);
26     int n,m;
27     scanf("%d%d",&n,&m);
28     memset(g,127,sizeof(g));
29     for(int i=1;i<=n;++i)
30       g[i][i]=0;
31     for(int i=1;i<=m;++i)
32     {
33         int x,y,l;
34         scanf("%d%d%d",&x,&y,&l);
35         g[x][y]=min(g[x][y],l);
36         g[y][x]=g[x][y];
37     }
38     memcpy(dis,g,sizeof(g));
39     long long ans=1e9+7;
40     for(int k=1;k<=n;++k)
41     {
42         for(int i=1;i<k;++i)
43         for(int j=i+1;j<k;++j)//因為k為環內最大點,所以只能循環到k-1
44         {
45             if(1LL*dis[i][j]+g[j][k]+g[k][i]<ans)
46             {
47                 ans=dis[i][j]+g[j][k]+g[k][i];
48                 toq=0;
49                 q[++toq]=i;
50                 getpath(i,j);
51                 q[++toq]=j;
52                 q[++toq]=k;
53             }
54         }
55         //然后是正常Floyd
56         for(int i=1;i<=n;++i)
57         for(int j=1;j<=n;++j)
58         {
59             if(1LL*dis[i][k]+dis[k][j]<dis[i][j])
60             {
61                 dis[i][j]=dis[i][k]+dis[k][j];
62                 zz[i][j]=k;
63             }
64         }
65     }
66     if(!toq){cout<<"No solution.";return 0;}
67     for(int i=1;i<=toq;++i)
68       cout<<q[i]<<" ";
69     return 0;
70 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM