并不会做,看了题解明白什么意思,学习到新的套路。
题意:给出一张图,你从s出发到t参观,每条边的边权代表速度,要求速度波动最小。
解法:
这道题是要求 最大速度/最小速度 尽量小,根据题目范围,我们完全可以枚举最大速度(从边中选取),此时我们要管的就是最小速度尽量大。
1.我们定好了最大速度Max,开始跑一遍kruskal,只把边权比枚举的最大速度小的边加入最小生成树。最小生成树用于记录现在的最小速度。
2.此时可以得到当前的答案即 Max / Min。如果当前答案更优则更新最大速度和最小速度。
3.直到t处的最小速度没有办法改变(比速度v小的边不足以组成s到t的道路),则退出。
为什么可以这样做呢?
假设最大速度从10开始枚举,此时得到最小速度为3,可行。我们不断降低这个最大速度,如果能存在一个更优的最小生成树,那么最小速度不受影响,但是我们让最大速度降低了(说白了就是这个边权为10的边根本就不在s到t的路上),让答案越来越逼近。
看到s到t,为什么不用最短路呢?最短路最重要的特征其实就是松弛条件dis【v】< dis【u】 + cost。但是这道题目里面我们是没有这种松弛条件的。所以无法用上最短路。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<utility>
#include<stack>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<set>
using namespace std;
const int maxn = 505;
const int maxm = 5005;
const double eps = 1e-6;
typedef pair<int, int> pii;
int n, m;
struct Node {
int u, v, c;
bool operator < (const Node& tmp) const {
if(c > tmp.c)
return 1;
return 0;
}
}edge[maxm];
int par[maxn];
int Find(int x) {
int root, tmp = x;
while(x != par[x])
x = par[x];
root = x;
x = tmp;
while(x != par[x]) {
tmp = par[x];
par[x] = root;
x = tmp;
}
return root;
}
void Union(int x, int y) {
par[y] = x;
}
int Min;
double kruskal(int s, int t, int id) {
for(int i = id; i < m; i++) {
int x = Find(edge[i].u);
int y = Find(edge[i].v);
if(x != y) {
Union(x, y);
}
if(Find(s) == Find(t)) {
return edge[i].c;
}
}
return -1;
}
void myprintf(int minlen, int maxlen) {
int gcd = __gcd(minlen, maxlen);
minlen /= gcd;
maxlen /= gcd;
if(minlen == 1)
printf("%d\n", maxlen);
else
printf("%d/%d\n", maxlen, minlen);
}
set <int> Set;
int main() {
scanf("%d%d", &n, &m);
for(int i = 0, u, v, val; i < m; i++) {
scanf("%d%d%d", &u, &v, &val);
edge[i].u = u, edge[i].v = v, edge[i].c = val;
}
int s, t;
double ans = 1e10;
int Max;
scanf("%d%d", &s, &t);
sort(edge, edge + m);
for(int i = 0; i < m; i++) {
int d = edge[i].c;
if(Set.find(d) == Set.end()) {
for(int j = 0; j <= n; j++)
par[j] = j;
double tMin = kruskal(s, t, i);
if(tMin + 1 < eps) {
break;
} else {
double tmp = 1.0 * edge[i].c / tMin;
if(ans - tmp > eps) {
Max = edge[i].c;
Min = tMin;
ans = tmp;
}
}
}
Set.insert(d);
}
if(abs(ans - 1e10) < eps)
printf("IMPOSSIBLE\n");
else {
// cout << Min << ' ' << Max << '\n';
myprintf(Min, Max);
}
return 0;
}