[HAOI 2006] 旅行

题目大意

给定一个 nn 个节点、mm 条边的无向图,每条边有一个权值 wiw_i。给你两个顶点 sstt ,求一条路径,使得路径上最大边和最小边的比值最小。如果 sstt 之间没有路径,输出 IMPOSSIBLE;否则输出这个比值,如果答案不为整数,表示成一个约分到最简的分数。

1n5001 \leqslant n \leqslant 500

1m5,0001 \leqslant m \leqslant 5,000

1wi<30,0001 \leqslant w_i < 30,000

题目链接

【HAOI 2006】旅行 - Luogu 2502

题解

对边进行排序,用类似 Kruscal 的方法,依次加入每条边,直至 sstt 连通,更新答案,枚举从哪条边开始添边即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <cstdio>
#include <cfloat>
#include <algorithm>
const int MAXN = 505;
const int MAXM = 5005;
struct Edge {
int u, v, w;
bool operator<(const Edge &another) const {
return w < another.w;
}
} E[MAXM];
struct UnionFindSet {
int fa[MAXN], n;
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
int p = find(x), q = find(y);
if (p == q) return;
fa[q] = p;
}
void init(int n) {
this->n = n;
for (int i = 1; i <= n; i++) fa[i] = i;
}
} ufs;
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) scanf("%d %d %d", &E[i].u, &E[i].v, &E[i].w);
std::sort(E, E + m);
int s, t;
scanf("%d %d", &s, &t);
double ans = DBL_MAX;
int ansMax, ansMin;
for (int i = 0; i < m; i++) {
ufs.init(n);
int min = E[i].w, max;
for (int j = i; j < m; j++) {
max = E[j].w;
ufs.merge(E[j].u, E[j].v);
if (ufs.find(s) == ufs.find(t)) {
if ((double) max / min < ans) {
ans = (double) max / min;
ansMax = max;
ansMin = min;
}
break;
}
}
}
if (ans == DBL_MAX) puts("IMPOSSIBLE");
else if ((ansMax / ansMin) * ansMin == ansMax) printf("%.0lf\n", ans);
else {
int g = gcd(ansMax, ansMin);
printf("%d/%d\n", ansMax / g, ansMin / g);
}
return 0;
}