Telephone Lines(通信线路)题解

蓝书最短路例题

题目描述:

在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站 Ai 和 Bi。
特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。
现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 Li。
电话公司正在举行优惠活动。
农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。
农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。
求至少用多少钱可以完成升级。

样例
输入:

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

算法1:(分层图 + 堆优化的dijkstra)

O(VlogE)其中E为(k+1)∗m,运用动态规划的思想,不同免费的边数对应不同的层,每层图结构相同,其中 用dist[v][j]存从1到v免费j条边的最短路(题意中是路径中第j+1大的边权)。
对于 j 不变 dist[v][j] = max(dist[u][j], w[u to v]).
对于 j += 1 的情况, 可以得到 dist[v][j+1] = min(dist[u][j], dist[v][j+1]
详见代码和注释。
python 代码

import sys
import heapq
"""解法1:分层图 + 堆优化的dijkstra, 运用动态规划的思想"""

inf = float("inf")
n,m,k = map(int, sys.stdin.readline().split())
g = {i:[] for i in range(1, n+1)}
vis = [[False]*(k+1) for _ in range(n+1)]
dist = [[inf]*(k+1) for _ in range(n+1)]

for _ in range(m):  #存一层的图,因为每层图结构是一样的
    u,v,w = map(int, sys.stdin.readline().split())
    g[u].append((v, w))
    g[v].append((u, w))

def dijkstra(r = 1):
    q = []
    dist[r][0] = 0
    heapq.heappush(q, (0, r, 0))    # (d, 点的编号, 第几层) 不同的层数代表免费几条边
    while q:
        x,i,j = heapq.heappop(q)
        if vis[i][j]: continue
        vis[i][j] = True
        for v,w in g[i]:
            if dist[v][j] > max(x, w):
                dist[v][j] = max(x, w)
                heapq.heappush(q, (dist[v][j], v, j))
            if j < k and dist[v][j+1] > x:
                dist[v][j+1] = dist[i][j]
                heapq.heappush(q, (dist[v][j+1], v, j+1))

dijkstra(1)
ans = inf
for i in range(k+1):
    ans = min(dist[n][i], ans)
print(ans if ans != inf else -1)


算法2(二分答案 + 双端队列BFS) O(Elog10^6+1)
 

定义一个t表示答案,将边权大于t的看为1,将边权小于等于t的堪为0,求一边对短路(双向广搜O(E)),dist[n]就为答案为t时所需免费线路的条数。
if t <= k: r = mid
else: l = mid + 1
python 代码

import sys
from collections import deque
"""解法2: 双向广搜 + 二分答案"""

inf = float("inf")
n,m,k = map(int, sys.stdin.readline().split())
g = {i:[] for i in range(1, n+1)}

for _ in range(m):  #存一层的图,因为每层图结构是一样的
    u,v,w = map(int, sys.stdin.readline().split())
    g[u].append((v, w))
    g[v].append((u, w))

def solve(t):
    dist = [inf] * (n + 1)
    dist[1] = 0
    q = deque()
    q.append(1)
    while q:
        u = q.popleft()
        for v,w in g[u]:
            w = 0 if w <= t else 1
            if dist[v] > dist[u] + w:
                dist[v] = dist[u] + w
                if w == 0: q.appendleft(v)
                else: q.append(v)
    return dist[n] <= k


l,r = 0, 1000001
while (l < r): #二分答案
    mid = (l+r)//2
    if solve(mid): r = mid
    else: l = mid + 1
print(l if l != 1000001 else -1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mister.Yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值