AcWing 观光之旅
Description
给定一张无向图,求图中一个至少包含3个点的环,环上的节点不重复,并且环上的边的长度之和最小。
该问题称为无向图的最小环问题。
你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。
Input
第一行包含两个整数N和M,表示无向图有N个点,M条边。
接下来M行,每行包含三个整数u,v,l,表示点u和点v之间有一条边,边长为l。
Output
- 输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出’No solution.’。
Sample Input
5 7 1 4 1 1 3 300 3 1 10 1 2 16 2 3 100 2 5 15 5 3 20
Sample Output
1 3 5 2
Data Size
- 1≤N≤100,
1≤M≤10000,
1≤l<500
题解:
- Floyd。
- 无向图求最小环用Floyd,有向图求最小环用Dijkstra。
- 我们知道,当Floyd做最短路时,枚举到k时,dis(i, j)表示的是i到j的最短距离(途中只走小于k的点)。那么我们就可以以k为环的中心。然后枚举两个点i, j表示k的左边点和右边点。那么这个环的权值就是dis(i, j) + a(i, k) + a(i, j),因为dis(i, j)能算出来是通过只走<k的点走出来的。所以i到j的最短路并没有与i -> k与j -> k的路径重合。因此是个环。
- 所以枚举k,k从1-n。这样就可以求出每种k情况的最小环。在这些最小环里取最小即可。
- 算法流程:
- 以k做中心,算出当前k为中心的最小环。
- 将k加入“可走点”,进行松弛操作。
- 代码细节:当数据类型是longlong时,用memset(0x3f)就为longlong最大值的一半,并不是int最大值的一半。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define N 105
using namespace std;
int n, m, ans = 0x3f3f3f3f;
int dis[N][N], a[N][N], mid[N][N];
vector<int> path;
void getPath(int x, int y)
{
if(!mid[x][y]) return;
getPath(x, mid[x][y]);
path.push_back(mid[x][y]);
getPath(mid[x][y], y);
}
int main()
{
cin >> n >> m;
memset(a, 0x3f, sizeof(a));
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; i++)
dis[i][i] = a[i][i] = 0;
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
a[u][v] = a[v][u] = min(w, a[u][v]);
dis[u][v] = dis[v][u] = a[u][v];
}
for(int k = 1; k <= n; k++)
{
for(int i = 1; i < k; i++)
for(int j = i + 1; j < k; j++)
if((long long)dis[i][j] + a[i][k] + a[k][j] < ans)
{
ans = dis[i][j] + a[i][k] + a[k][j];
path.clear();
path.push_back(i);
getPath(i, j);
path.push_back(j);
path.push_back(k);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i != j && j != k && i != k)
if(dis[i][k] + dis[k][j] < dis[i][j])
{
dis[i][j] = dis[i][k] + dis[k][j];
mid[i][j] = k;
}
}
if(ans == 0x3f3f3f3f) cout << "No solution.";
else for(int i = 0; i < path.size(); i++) cout << path[i] << ' ';
return 0;
}