AcWing 340. 通信线路

AcWing 340. 通信线路

一、题目描述

在郊区有 N 座通信基站,P双向 电缆,第 i 条电缆连接基站 AiBi

特别地,1号基站是通信公司的总站 (起点),N号基站 (终点) 位于一座农场中。

现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 Li

电话公司正在举行优惠活动

农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司 免费 提供升级服务

农场主只需要支付在该路径上 剩余的电缆中升级价格最贵 的那条电缆的花费即可

至少用多少钱 可以完成升级

输入格式
1 行:三个整数 NPK

2..P+1 行:第 i+1 行包含三个整数 Ai,Bi,Li

输出格式
包含一个整数表示最少花费。

1 号基站与 N 号基站之间不存在路径,则输出 1

数据范围
0K<N1000,1P10000,1Li1000000

输入样例

5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6

输出样例

4

二、题目解析

理解题意

找一条路径,边权最大的k条边忽略,k+1大的边权作为该条路径的代价求最小代价

思考过程

① 从1号点出发,没有路可以到达n点, 无解,输出1

② 如果 最短路径边数注意:不是路径的加权和)不超过k条,含义:不用花钱就可以升级线路, 输出0

③ 上面 ②中给我们提出了一个新概念:路径边数,我们知道,如果想计算获取 路径边数,常见的办法是设置边权为1。 那是不是所有边都设置为边权为1呢?好像不行,因为这样的话,那人家还给你修每条路径的钱数就没用上啊,而且你也没有办法求出你的最小支出啊,此路不通。

④ 这就很纠结啊:不设边权为1,无法知道路径长度;全设边权为1,就会丢失关键信息。只能是设置 部分 边权为1

⑤ 那啥样的边权为1,啥样的边权为0呢?还得用上真实的边权概念!此时,有如下猜想:

如果给我mid元钱,我有没有办法确定这么多钱能否够完成升级一条线路呢?
这个简单,我们可以视真实边权大于mid的设置 虚拟边权1,反之设为0
然后在这个图上用Dijkstra求最短路径,也就是最短路径长度:

  • 如果最短路径的长度值大于k,说明mid小了,再调大一点
  • 如果最短路径的长度值不大于k,说明mid大了,再调小一点

噢,原来需要 二分答案

Code

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1010;  // 1000个点
const int M = 20010; // 10000条,记录无向边需要两倍空间
int idx, h[N], e[M], w[M], ne[M];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int n; // 点数
int m; // 边数

int k; // 不超过K条电缆,由电话公司免费提供升级服务

bool st[N]; // 记录是不是在队列中
int dis[N]; // 记录最短距离

// mid指的是我们现在选最小花费
bool check(int mid) {
    // 需要跑多次dijkstra,所以需要清空状态数组
    memset(st, false, sizeof st);
    memset(dis, 0x3f, sizeof dis);
    priority_queue<PII, vector<PII>, greater<PII>> q;
    dis[1] = 0;
    q.push({0, 1});

    while (q.size()) {
        PII t = q.top();
        q.pop();
        int u = t.second;
        if (st[u]) continue;
        st[u] = true;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            int v = w[i] > mid; // 如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0
            if (dis[j] > dis[u] + v) {
                dis[j] = dis[u] + v;
                q.push({dis[j], j});
            }
        }
    }
    // 如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达
    if (dis[n] == INF) {
        puts("-1"); // 不可达,直接输出-1
        exit(0);
    }
    return dis[n] <= k; // 如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法
}
int main() {
    memset(h, -1, sizeof h);
    cin >> n >> m >> k;
    int a, b, c;
    for (int i = 0; i < m; i++) {
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    /*
    这里二分的是直接面对答案设问: 至少用多少钱 可以完成升级
    依题意,最少花费其实是所有可能的路径中,第k+1条边的花费
    如果某条路径不存在k+1条边(边数小于k+1),此时花费为0
    同时,任意一条边的花费不会大于1e6,所以,这里二分枚举范围:0 ~ 1e6
    */
    int l = 0, r = 1e6;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (check(mid)) // check函数的意义:如果当前花费可以满足要求,那么尝试更小的花费
            r = mid;
        else
            l = mid + 1;
    }
    printf("%d\n", l);
    return 0;
}
posted @   糖豆爸爸  阅读(132)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2018-03-16 TeamViewer的下载地址,低调低调
2018-03-16 Windows开机自动启动pageant,方便使用ssh链接到GitHub
Live2D
点击右上角即可分享
微信分享提示