1. Floyd算法
- 作用:用于求解多源最短路,可以求解出任意两点的最短路
- 利用动态规划只需三重循环即可(动态规划可以把问题求解分为多个阶段)
- 定义dp[k][i][j]表示点i到点j的路径(除去起点终点)中最大编号不超过k的情况下,点i到点j的最短距离。
- 当加入第k个点作为i到j的中间点:
发现可以使用滚动数组优化第一维度:
枚举所有k,判断是否可以作为中间点,可以作为中间点则优化最短路。
初始化:如果<i, j>无边,则dp[i][j] = INF, 有边则等于边权;dp[i][i] = 0(自己到自己是不用走的)
为了理解更深刻,简单举个例子:
各点之间的关系用邻接矩阵保存(下图中又两个邻接矩阵,一个是两点之间的最短距离,还有一个是两点之间的最短路中经过的节点。)
更新
每次基于之前能找到的最短路径,如果比它短就更新。
以2号节点作为中转站是基于1号节点作为中转站的,经过n轮递推就可以得到最终答案(任意两点的最短路)
例题:
蓝桥1121
为什么先遍历k,之后遍历i,j?
因为要符合顺序,遍历完中间点后, 就要遍历邻接矩阵,进行最短距离的更新。
import os
import sys
# 请在此输入您的代码
n, m, q = map(int, input().split())
INF = 10 ** 18
# dp[i][j]表示i到j的最短路
dp = [[INF] * (n + 1) for i in range(n + 1)] # 初始值设较大值
for i in range(1, n + 1):
dp[i][i] = 0 # 自己到自己的距离为0
for _ in range(m):
u, v, w = map(int, input().split())
dp[u][v] = dp[v][u] = min(dp[u][v], w) # 双向边/无向边(可能有重边)
# Floyd算法模板
# dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])
for k in range(1, n + 1):
for i in range(1, n + 1):
for j in range(1, n + 1):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])
for _ in range(q):
u, v = map(int, input().split())
if dp[u][v] == INF:
print(-1)
else:
print(dp[u][v])
蓝桥8336
import os
import sys
# 请在此输入您的代码
"""
翻译题意:有n个城市,m条边就是有m条路径可以流通,每个城市有自己的商品产出,可以拿去别的地方销售,
需要求出最大利润,但是商品产量ai是不变的,生产成本pi也是不变的,只有售卖单价会随着商品运输到其他
城市会改变,以及带来的运输费用(这里的运输费用有一个路径的问题,需要用最短路算出来最少支付。
"""
n, m = map(int, input().split())
# g = s - p - f(路径费用)
INF = 10 ** 17
a, p, s = [0] * (n + 1), [0] * (n + 1), [0] * (n + 1) # 商品的产量, 生产成本, 售卖单价
f = [[INF] * (n + 1) for i in range(n + 1)] # 记录最短路(也就是最短的运输费用)
g = [[0] * (n + 1) for i in range(n + 1)] # 记录利润
for i in range(1, n + 1):
a[i], p[i], s[i] = map(int, input().split())
for _ in range(1, m + 1):
u, v, w = map(int, input().split())
f[u][v] = f[v][u] = min(f[u][v], w)
for i in range(1, n + 1):
f[i][i] = 0
for k in range(1, n + 1):
for i in range(1, n + 1):
for j in range(1, n + 1):
f[i][j] = min(f[i][k] + f[k][j], f[i][j])
# g[i][j]表示城市1的物品运输到城市j可得的利润=城市j的售价-城市i的成本-运输f[i][j]
for i in range(1, n + 1):
for j in range(1, n + 1):
g[i][j] = s[j] - p[i] - f[i][j]
ans = 0
for i in range(1, n + 1):
# 遍历每个城市的商品
now_ans = 0
# 遍历移动到的城市(包括自己本身)
for j in range(1, n + 1):
now_ans = max(now_ans, a[i] * g[i][j])
ans += now_ans # 记录每个城市的利润
print(ans)
蓝桥4218
import os
import sys
'''
floyd算法小变形,题目的意思是两个人要先找到一个地方会面,之后再各自前往各自的目的地.
所以这个会面的时间是由晚到的那个人决定的,(同时从这个时间开始出发)
最终最早的最晚到达时间也是由最晚的那个人决定的
这就是为什么选取两个最大值求和的答案再跟原来的答案作比较,取最小值.
ans = min(ans,max(dp[s1][i],dp[s2][i])+max(dp[i][e1],dp[i][e2]))
'''
# 请在此输入您的代码
n, m = map(int, input().split())
G = [[] * (n + 1) for _ in range(n + 1)]
inf = 10 ** 18
dp = [[inf] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
dp[i][i] = 0
for _ in range(m):
a, b, t = map(int, input().split())
dp[a][b] = dp[b][a] = min(dp[a][b], t)
s1, e1, s2, e2 = map(int, input().split())
for k in range(1, n + 1):
for i in range(1, n + 1):
for j in range(1, n + 1):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])
ans = inf
for i in range(1,n+1):
ans = min(ans,max(dp[s1][i],dp[s2][i])+max(dp[i][e1],dp[i][e2]))
if ans==inf:
print(-1)
else:
print(ans)
总结下解题步骤:
- 初始化邻接矩阵(有边直接连接的直接存,没有的存INF最大值,自己到自己的路径长度为0)
- 遍历(k,i,j)更新i到j的最短路,通过k
- 依据题意更新答案
2. Dijkstra算法
作用:处理非负权边的单源最短路问题
利用贪心+动态规划思想,实现从源点s出发到所有点的最短距离
核心思想:从起点出发,每次选择距离最短的点进行”松弛”操作
算法步骤:
1.将起点入队列,d数组表示从起点s出发到达每个最短距离
2.不断取出队列中距离最小的点u,进行“松弛”:
对于从u到v,权重为w的边
例题:
蓝桥1122
import os
import sys
# 请在此输入您的代码
from queue import PriorityQueue # 导入优先队列(出队列先出最短的)
INF = 10 ** 18
def dijkstra(s):
# 返回从s到所有点的最短路
d = [INF] * (n + 1)
vis = [0] * (n + 1) # 只有第一次出队列是有用的,这里与BFS不同,是标记出队列
q = PriorityQueue()
# 1. 将起点入队列,更新距离
d[s] = 0
q.put((d[s], s))
# 2. 当队列非空
while not q.empty():
dis, u = q.get()
if vis[u]:
continue
vis[u] = 1
# 对于从u出发,到达v,权重为w的边
for v, w in G[u]:
# 松弛
if d[v] > d[u] + w:
d[v] = d[u] + w
q.put((d[v], v))
for i in range(1, n + 1):
if d[i] == INF:
d[i] = -1
return d[1::]
n, m = map(int, input().split())
G = [[] for i in range(n + 1)]
for i in range(m):
u, v, w = map(int, input().split())
G[u].append((v, w))
print(*dijkstra(1))
蓝桥3818
"""
题目解读:
从起点坐标移动至终点坐标,经过的恶魔果实消耗能量加起来不能超过E,求一个到终点的最短路,
只不过这里的边是消耗的能量
"""
import sys
input = sys.stdin.re adline
from queue import PriorityQueue # 导入优先队列(出队列先出最短的)
INF = 10 ** 18
def get(c):
if c == '.':
return 0
else:
return ord(c) - ord('A') + 1
def dijkstra():
# 返回从s到所有点的最短路
d = [[INF] * m for i in range(n)]
vis = [[0] * m for i in range(n)] # 只有第一次出队列是有用的,这里与BFS不同,是标记出队列
q = PriorityQueue()
# 1. 将起点入队列,更新距离
d[x1][y1] = 0
q.put((d[x1][y1], x1, y1))
# 2. 当队列非空
while not q.empty():
dis, x, y = q.get() # 取出最小距离的点
if x == x2 and y == y2: # 如果是终点,直接返回距离
return dis
if vis[x][y]:
continue
vis[x][y] = 1
# 对于从u出发,到达v,权重为w的边
for dx, dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
xx, yy = x + dx, y + dy
# 判断越界和标记,障碍物
if 0 <= xx < n and 0 <= yy < m and vis[xx][yy] == 0 and Map[xx][yy] != '#':
if d[xx][yy] > d[x][y] + get(Map[xx][yy]):
d[xx][yy] = d[x][y] + get(Map[xx][yy])
q.put((d[xx][yy], xx, yy))
return INF
n, m = map(int, input().split())
x1, y1, x2, y2 = map(int, input().split())
x1, y1, x2, y2 = x1 - 1, y1 - 1, x2 - 1, y2 - 1
Map = [input() for i in range(n)]
E = int(input())
if E >= dijkstra():
print("Yes")
else:
print("No")
正在更新中...