题目链接🔗:340. 通信线路 - AcWing题库
分析:
思路如下:
因为题目要求的是:从1到N的路线上,第K+1长的路最小,所以我们可以二分枚举边权,看是否有小于等于K条路的边权大于它。
对于每个枚举的答案,做一次双端队列的bfs,节点1作为起点,返回一个布尔值:走到N的最短路中,是否有小于等于K条路的边权是大于被枚举的边权的
若返回True,说明有小于等于K条路的边权是大于被枚举的边权,所以right=mid,即继续往下枚举边权,反之则像大枚举边权。
更多细节请看注释~
import collections
INF = float('inf')
N,P,K = map(int,input().split())
Map = [[0 for i in range(N+1)]for j in range(N+1)]
for i in range(P) :
a,b,c = map(int,input().split())
Map[a][b] = Map[b][a] = c # 无向图存边
def check(bound) :
flag = [1 for i in range(N+1)] # 判断当前点是否被用过
dis = [INF for i in range(N+1)] # 当前点到起点的最短距离
dis[1] = 0 # 起点到起点的距离为0
queue = collections.deque() # 双端队列存储当前点
queue.append(1)
while queue :
node = queue.popleft()
if not flag[node] : continue # 这里需要注意 :一定要在出队列时判重,而不是入队列的时候,否则答案可能不正确
flag[node] = 0 # 这个点已经被用过了
for i in range(1,N+1) :
if Map[node][i] : # 如果有边
tmp = Map[node][i] > bound # 关键点:如果当前边权是大于bound(二分值)的,我们把这个边权设为1,反正为0
if dis[i] > dis[node] + tmp : # 如果可以更新答案
dis[i] = dis[node] + tmp
if tmp : queue.append(i) # 如果tmp==1 : 将这个点放入队尾,反之放入队头
else : queue.appendleft(i)
return dis[N] <= K # 返回是否有小于等于K条路的边权是大于被枚举的边权
left,right = 0,10**6+1 # 二分的端点,不取10**6作为右端点,是因为如果结果输出为10**6,不能判断是无解还是答案确实为10**6
while left < right :
mid = (left+right)>>1 # 二分
if check(mid) : right = mid
else : left = mid + 1
if right == 10**6 + 1 : right = -1 # 判断是否无解
print(right)