题目
给定一张无向图,求图中一个至少包含
个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。若无解,输出 No solution.
图的节点数不超过
。
- 输入格式
第一行两个正整数 表示点数和边数。
接下来 行,每行三个正整数 ,表示节点 之间有一条长度为 的边。 - 输出格式
输出一个最小环的方案:按环上顺序输出最小环上的点。若最小环不唯一,输出任意一个均可。若无解,输出 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
题解:
1.求环
这道题不是特别难,感觉就像是模拟题升高“亿点点”。首先我们要知道如何求出环状最短路,最简单的我们可以想到将一个最短路先算出来,左右端点为i,j,再枚举一个k点,连接起来就是一个环,但问题就在于i,j枚举范围,如果k在i,j中间,那么就会出现i --> k --> j,从而连起来不是一个环而是一条通路,所以我们不难想到第一重阶段枚举k在(1——n)的取值,而后两重循环枚举i, j(1——k - 1),这样即可使k一定不在i,j之间,所以联通出来一定是个环。最后再来一个循环枚举i——j的经k点最短路,为下一次求环做输出路径和最短路做基础。
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)fd[i][j] + w[i][k] + w[j][k] < _min) {
//每次找最小
//一个环i,j,k的距离为i——j + i——k + j —— k;
_min = (long long)fd[i][j] + w[i][k] + w[j][k];
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if(fd[i][k] + fd[k][j] < fd[i][j]) {
fd[i][j] = fd[i][k] + fd[k][j];
pre[i][j] = pre[k][j];//保存路径
}
}
}
}
2.路径:
路径可以用一个不定长数组(vector)来存,之前讲过,枚举环的三个点:i, j, k,如果我们找到一个新的环,它比之前的费用都小,那么就可以把它的路径push()进去,注意分别要压入起点i,i——j最小路径,k。
if(!ve.empty()) {//清空
ve.clear();
}
ve.push_back(i);
charu(i, j);
ve.push_back(k);
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
using namespace std;
int n, m, k, pre[105][105], t, d, u, v, fd[105][105], w[105][105];
vector<int>ve;
void charu (int s, int xx) {
if (pre[s][xx] == 0) {
return;
}
charu(s, pre[s][xx]);
ve.push_back(xx);
}
int main() {
memset(fd, 0x3f, sizeof(fd));
memset(w, 0x3f, sizeof(w));
scanf("%d %d", &n, &m);
for (int i = 1;i <= m; i++) {
scanf("%d %d %d", &u, &v, &d);
fd[u][v] = min(fd[u][v], d);
fd[v][u] = min(fd[v][u], d);
w[u][v] = min(w[u][v], d);
w[v][u] = min(w[v][u], d);
pre[u][v] = u;
pre[v][u] = v;
}
for(int i = 1;i <= n; i++) {
fd[i][i] = 0;
w[i][i] = 0;
}
int _min = 0x3f3f3f3f;
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)fd[i][j] + w[i][k] + w[j][k] < _min) {
_min = (long long)fd[i][j] + w[i][k] + w[j][k];
if(!ve.empty()) {
ve.clear();
}
ve.push_back(i);
charu(i, j);
ve.push_back(k);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if(fd[i][k] + fd[k][j] < fd[i][j]) {
fd[i][j] = fd[i][k] + fd[k][j];
pre[i][j] = pre[k][j];
}
}
}
}
if(ve.empty()) {
printf("No solution.");
return 0;
}
for(int i = 0;i < ve.size(); i++) {
printf("%d ", ve[i]);
}
return 0;
}