CODE[VS] 1001 舒适的路线 (kruskal)

本文介绍了一种求解从起点到终点路径上速度波动最小的问题。通过枚举最大速度并使用Kruskal算法来确定最小生成树,进而找到最优解。文章详细解释了解题思路,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

并不会做,看了题解明白什么意思,学习到新的套路。

题意:给出一张图,你从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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值