蓝书最短路例题
题目描述:
在郊区有 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)
定义一个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)