Sightseeing Trip题解(图论)

题目

给定一张无向图,求图中一个至少包含 3 3 个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。若无解,输出 No solution.
图的节点数不超过 100 100

  • 输入格式
    第一行两个正整数 n , m n,m 表示点数和边数。
    接下来 m m 行,每行三个正整数 x , y , z x, y, z ,表示节点 x , y x, y 之间有一条长度为 z z 的边。
  • 输出格式
    输出一个最小环的方案:按环上顺序输出最小环上的点。若最小环不唯一,输出任意一个均可。若无解,输出 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;
} 

猜你喜欢

转载自blog.csdn.net/cqbz_lipengcheng/article/details/107677918
今日推荐