「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 }
原文地址:https://www.cnblogs.com/qwerta/p/9833391.html