题目地址:
https://www.luogu.com.cn/problem/P6175
题目描述:
给定一张无向图,求图中一个至少包含
3
3
3个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小的环的边权和。若无解,输出No solution.
。
输入格式:
第一行两个正整数
n
,
m
n,m
n,m表示点数和边数。接下来
m
m
m行,每行三个正整数
u
,
v
,
d
u,v,d
u,v,d,表示节点
u
,
v
u,v
u,v之间有一条长度为
d
d
d的边。
输出格式:
输出边权和最小的环的边权和。若无解,输出No solution.
。
数据范围:
对于
20
%
20\%
20%的数据,
n
,
m
≤
10
n,m \leq 10
n,m≤10。
对于
60
%
60\%
60%的数据,
m
≤
100
m\leq 100
m≤100。
对于
100
%
100\%
100%的数据,
1
≤
n
≤
100
1\le n\leq 100
1≤n≤100,
1
≤
m
≤
5
×
1
0
3
1\le m\leq 5\times 10^3
1≤m≤5×103,
1
≤
d
≤
1
0
5
1 \leq d \leq 10^5
1≤d≤105。
思路是Floyd算法,参考https://blog.csdn.net/qq_46105170/article/details/113821689。Floyd算法是求点对最短路的算法,设 g [ i ] [ j ] g[i][j] g[i][j]是 i → j i\to j i→j的最小边权(如果有平行边,取边权最小者),设 f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]是从 i i i走到 j j j途中只经过 ≤ k \le k ≤k编号的点的情况下的最短路径长度,那么在求出 f [ k − 1 ] f[k-1] f[k−1]之后,环上点最大编号为 k k k的那些环的最小总边权就是: min i < j { f [ k − 1 ] [ i ] [ j ] + g [ i ] [ k ] + g [ k ] [ j ] } \min_{i<j}\{f[k-1][i][j]+g[i][k]+g[k][j]\} i<jmin{f[k−1][i][j]+g[i][k]+g[k][j]}枚举完所有的 k k k,即得答案。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
// 注意,这里不能像Floyd算法里那样直接在g上操作,而要开出点对距离数组d,
// 因为边长这个信息是需要用到的
int g[N][N], d[N][N];
int floyd() {
int res = INF;
for (int k = 1; k <= n; k++) {
for (int i = 1; i < k; i++)
for (int j = i + 1; j < k; j++)
// 防止溢出
if (d[i][j] < INF && g[j][k] < INF && g[k][i] < INF)
res = min(res, d[i][j] + g[j][k] + g[k][i]);
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
d[i][j] = d[j][i] = min(d[i][j], d[i][k] + d[k][j]);
}
return res;
}
int main() {
memset(g, 0x3f, sizeof g);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) g[i][i] = 0;
while (m--) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
memcpy(d, g, sizeof g);
int res = floyd();
res < INF ? printf("%d\n", res) : puts("No solution.");
}
时间复杂度 O ( n 3 ) O(n^3) O(n3),空间 O ( n 2 ) O(n^2) O(n2)。