代码随想录算法训练营day76 | Floyd 算法精讲、A * 算法精讲

本次题目来自于卡码网

 ​​97. 小明逛公园 (Floyd 算法精讲)

1、确定dp数组以及下标的含义

grid[i][j][k] = m,表示 节点i 到 节点j 以[1...k] 集合为中间节点的最短距离为m

2、确定递推公式

分两种情况:

  • 节点i 到 节点j 的最短路径经过节点k
  • 节点i 到 节点j 的最短路径不经过节点k

对于第一种情况,grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]

第二种情况,grid[i][j][k] = grid[i][j][k - 1]

因为我们是求最短路,对于这两种情况自然是取最小值。

即: grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])

3、dp数组如何初始化

把k 赋值为 0,本题 节点0 是无意义的,节点是从1 到 n

初始化代码

vector<vector<vector<int>>> grid(n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1, 10005)));  // C++定义了一个三位数组,10005是因为边的最大距离是10^4

for(int i = 0; i < m; i++){
    cin >> p1 >> p2 >> val;
    grid[p1][p2][0] = val;
    grid[p2][p1][0] = val; // 注意这里是双向图
} 

grid数组中其他元素数值应该初始化多少呢? 本题求的是最小值,所以输入数据没有涉及到的节点的情况都应该初始为一个最大数。

4、确定遍历顺序

我们来看初始化,我们是把 k =0 的 i 和j 对应的数值都初始化了,这样才能去计算 k = 1 的时候 i 和 j 对应的数值。

这就好比是一个三维坐标,i 和j 是平层,而k 是 垂直向上 的。

遍历的顺序是从底向上 一层一层去遍历。

所以遍历k 的for循环一定是在最外面,这样才能一层一层去遍历

5、举例推导dp数组

if __name__ == '__main__':
    n, m = map(int, input().split())
    grid = [[[10005] * (n + 1) for _ in range(n + 1)] for _ in range(n + 1)]  # 因为边的最大距离是10^4

    for i in range(m):
        p1, p2, val = map(int, input().split())
        grid[p1][p2][0] = val
        grid[p2][p1][0] = val  # 双向图

    # 开始 floyd
    for k in range(1, n + 1):
        for i in range(1, n + 1):
            for j in range(n + 1):
                grid[i][j][k] = min(grid[i][j][k - 1], grid[i][k][k - 1] + grid[k][j][k - 1])

    # 输出结果
    z = int(input())
    for _ in range(z):
        start, end = map(int, input().split())
        if grid[start][end][n] == 10005:
            print(-1)
        else:
            print(grid[start][end][n])

空间优化

我们可以做一下 空间上的优化,从滚动数组的角度来看,我们定义一个 grid[n + 1][ n + 1][2] 这么大的数组就可以,因为k 只是依赖于 k-1的状态,并不需要记录k-2,k-3,k-4 等等这些状态。

又由于本层计算中,使用了本层计算过的 grid[i][k] 和 grid[k][j] 是没问题的。那么就没必要区分,grid[i][k] 和 grid[k][j] 是 属于 k - 1 层的呢,还是 k 层的。

if __name__ == '__main__':
    n, m = map(int, input().split())
    grid = [[10005] * (n + 1) for _ in range(n + 1)]  # 因为边的最大距离是10^4

    for i in range(m):
        p1, p2, val = map(int, input().split())
        grid[p1][p2] = val
        grid[p2][p1] = val  # 双向图

    # 开始 floyd
    for k in range(1, n + 1):
        for i in range(1, n + 1):
            for j in range(n + 1):
                grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j])

    # 输出结果
    z = int(input())
    for _ in range(z):
        start, end = map(int, input().split())
        if grid[start][end] == 10005:
            print(-1)
        else:
            print(grid[start][end])

126. 骑士的攻击 (A * 算法精讲)

加入了启发式函数,使用了优先队列,优先队列中自定义了比较函数(https://www.cnblogs.com/xrszff/p/14783972.html)

import heapq

# F = G + H
# G = 从起点到该节点路径消耗
# H = 该节点到终点的预估消耗


class Knight:
    def __init__(self):
        self.x = 0
        self.y = 0
        self.g = 0
        self.h = 0
        self.f = 0

    def __lt__(self, other):  # 自定义比较函数
        return self.f < other.f


def heuristic(k):  # 欧拉距离
    return (k.x - b1) * (k.x - b1) + (k.y - b2) * (k.y - b2)  # 统一不开根号,这样可以提高精度


def astar(k):
    heapq.heappush(que, k)
    while que:
        cur = heapq.heappop(que)
        if cur.x == b1 and cur.y == b2:
            break
        for i in range(8):
            next = Knight()
            next.x = cur.x + dir[i][0]
            next.y = cur.y + dir[i][1]
            if next.x < 1 or next.x > 1000 or next.y < 1 or next.y > 1000:
                continue
            if moves[next.x][next.y] == 0:
                moves[next.x][next.y] = moves[cur.x][cur.y] + 1

                # 开始计算F
                next.g = cur.g + 5  # 统一不开根号,这样可以提高精度,马走日,1 * 1 + 2 * 2 = 5
                next.h = heuristic(next)
                next.f = next.g + next.h
                heapq.heappush(que, next)


if __name__ == '__main__':
    dir = [(-2, -1), (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2)]
    que = []
    heapq.heapify(que)
    n = int(input())
    for _ in range(n):
        a1, a2, b1, b2 = map(int, input().split())
        moves = [[0] * 1001 for _ in range(1001)]  # 每次重新开辟空间
        start = Knight()
        start.x = a1
        start.y = a2
        start.g = 0
        start.h = heuristic(start)
        start.f = start.g + start.h
        astar(start)
        while que:
            heapq.heappop(que)  # 队列清空
        print(moves[b1][b2])

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 17:50:07       5 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 17:50:07       5 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 17:50:07       4 阅读
  4. Python语言-面向对象

    2024-07-10 17:50:07       6 阅读

热门阅读

  1. docker安装tomcat容器

    2024-07-10 17:50:07       11 阅读
  2. 线段树动态开点

    2024-07-10 17:50:07       10 阅读
  3. 代码随想录算法训练营:29/60

    2024-07-10 17:50:07       8 阅读
  4. Postman接口测试工具详解

    2024-07-10 17:50:07       13 阅读
  5. 逻辑回归的损失函数

    2024-07-10 17:50:07       10 阅读
  6. postman接口测试工具详解

    2024-07-10 17:50:07       11 阅读
  7. SQL语法(DQL):SELECT 多表查询之子查询

    2024-07-10 17:50:07       11 阅读
  8. 获取和设置Spring Cookie

    2024-07-10 17:50:07       11 阅读
  9. Spring——配置说明

    2024-07-10 17:50:07       9 阅读
  10. springboot中在filter中用threadlocal存放用户身份信息

    2024-07-10 17:50:07       16 阅读
  11. LDAP技术解析:打造安全、高效的企业数据架构

    2024-07-10 17:50:07       12 阅读
  12. android 替换设置-安全里面的指纹背景图片

    2024-07-10 17:50:07       14 阅读