题目描述
输入格式
输出格式
样例
题解
无向图的最小环问题啊,不知道在多少场考试中间卡住了我的临门一脚。——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.2 例 1」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 }