Day62--图论--97. 小明逛公园(卡码网),127. 骑士的攻击(卡码网)

Day62--图论--97. 小明逛公园(卡码网),127. 骑士的攻击(卡码网)

97. 小明逛公园(卡码网)

Floyd 算法对边的权值正负没有要求,都可以处理。

方法:Floyd算法(三维版本)

思路:

动态规划五步曲:

  1. 确定dp数组含义:grid[i][j][k] = m表示 节点i 到 节点j 以k节点为中间节点的最短距离为m。(k取值自[1,k])
  2. 递推公式:
    1. 节点i 到 节点j 的最短路径经过节点k
      • 先从i走到k,再从k走到 j :grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1](这里可以理解为"蹭了一下"。从全局来看,这条路是"经过了k"的。但是分成两段来看,从i到k,是不经过k的,从k到j,也是不经过k的,所以取k-1)
    2. 节点i 到 节点j 的最短路径不经过节点k
      • 不走k:grid[i][j][k] = grid[i][j][k - 1]
  3. 初始化:
  4. 遍历顺序:从递推公式看,k 依赖于 k - 1, i 和j 的到 并不依赖与 i - 1 或者 j - 1 。或者可以这么理解,i和j只是图的遍历,而k才是动态规划里面的i的遍历。所以k要在最外层for。
java 复制代码
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();

        int[][][] graph = new int[n + 1][n + 1][n + 1];

        // 初始化为不可达状态
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                for (int k = 0; k <= n; k++) {
                    // 自身到自身的距离为0
                    if (i == j) {
                        graph[i][j][k] = 0;
                    } else {
                        graph[i][j][k] = 10005;
                    }
                }
            }
        }

        for (int i = 0; i < m; i++) {
            int x = in.nextInt();
            int y = in.nextInt();
            int val = in.nextInt();
            // 双向图
            graph[x][y][0] = val;
            graph[y][x][0] = val;
        }

        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    graph[i][j][k] =
                            Math.min(graph[i][j][k - 1], graph[i][k][k - 1] + graph[k][j][k - 1]);
                }
            }
        }

        int count = in.nextInt();
        while (count-- > 0) {
            int start = in.nextInt();
            int end = in.nextInt();
            int res = graph[start][end][n];
            System.out.println(res == 10005 ? -1 : res);
        }
    }
}

方法:Floyd算法(二维版本)

思路:

可以看到k只依赖于k-1,与k-2,k-3等无关,所以只需要两个变量就可以了。空间上,k这一维度可以灭掉。

写完这个版本之后,观察代码,其实这就是一道很简单的动态规划题,只不过维度比普通的动态规划要多了一维,看起来恐怖了而已。

对于graph[i][j]怎么理解?

其实就是i到j怎么走?观察上层:就从i走到k,从k走到j会快一点吗?不会的话,按照原来的规划走。

java 复制代码
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();

        int[][] graph = new int[n + 1][n + 1];

        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                // 自身到自身的距离为0
                if (i == j) {
                    graph[i][j] = 0;
                } else {
                    graph[i][j] = 10005;
                }
            }
        }

        for (int i = 0; i < m; i++) {
            int x = in.nextInt();
            int y = in.nextInt();
            int val = in.nextInt();
            // 双向图
            graph[x][y] = val;
            graph[y][x] = val;
        }

        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    // 这里其实隐藏了一个[k-1],也就是动态规划里面的上一层。
                    // i到j怎么走?观察上层:就从i走到k,从k走到j会快一点吗?不会的话,按照原来的规划走。
                    graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
                }
            }
        }

        int count = in.nextInt();
        while (count-- > 0) {
            int start = in.nextInt();
            int end = in.nextInt();
            int res = graph[start][end];
            System.out.println(res == 10005 ? -1 : res);
        }
    }
}

127. 骑士的攻击(卡码网)

方法:A star算法

思路:

第一轮刷题先跳过了。远超能理解的程度。

java 复制代码
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
    static int[][] moves = new int[1001][1001];
    static int[][] dir = {{-2, -1}, {-2, 1}, {-1, 2}, {1, 2}, 
                          {2, 1}, {2, -1}, {1, -2}, {-1, -2}};
    static int b1, b2;
    
    // F = G + H
    // G = 从起点到该节点路径消耗
    // H = 该节点到终点的预估消耗
    
    static class Knight implements Comparable<Knight> {
        int x, y;
        int g, h, f;
        
        @Override
        public int compareTo(Knight k) {  // 重载比较器,实现最小堆(按f值从小到大)
            return Integer.compare(this.f, k.f);
        }
    }
    
    static PriorityQueue<Knight> que = new PriorityQueue<>();
    
    static int Heuristic(Knight k) { // 欧拉距离
        return (k.x - b1) * (k.x - b1) + (k.y - b2) * (k.y - b2); // 统一不开根号,这样可以提高精度
    }
    
    static void astar(Knight k) {
        Knight cur, next;
        que.add(k);
        moves[k.x][k.y] = 1;  // 修正:起点需要标记为已访问,避免重复处理
        
        while (!que.isEmpty()) {
            cur = que.poll();
            if (cur.x == b1 && cur.y == b2)
                break;
            for (int i = 0; i < 8; i++) {
                next = new Knight();
                next.x = cur.x + dir[i][0];
                next.y = cur.y + dir[i][1];
                if (next.x < 1 || next.x > 1000 || next.y < 1 || 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;
                    que.add(next);
                }
            }
        }
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        while (n-- > 0) {
            int a1 = scanner.nextInt();
            int a2 = scanner.nextInt();
            b1 = scanner.nextInt();
            b2 = scanner.nextInt();
            
            // 重置moves数组
            for (int i = 0; i < 1001; i++) {
                for (int j = 0; j < 1001; j++) {
                    moves[i][j] = 0;
                }
            }
            
            // 清空队列(处理多组测试用例)
            que.clear();
            
            Knight start = new Knight();
            start.x = a1;
            start.y = a2;
            start.g = 0;
            start.h = Heuristic(start);
            start.f = start.g + start.h;
            
            astar(start);
            
            // 输出结果时减1,因为我们给起点加了1
            System.out.println(moves[b1][b2] - 1);
        }
        scanner.close();
    }
}
相关推荐
yi.Ist5 小时前
图论——Djikstra最短路
数据结构·学习·算法·图论·好难
KarrySmile5 小时前
Day55--图论--107. 寻找存在的路径(卡码网)
图论·并查集·寻找存在的路径
Morriser莫2 天前
图论Day2学习心得
算法·图论
KarrySmile2 天前
Day53--图论--106. 岛屿的周长(卡码网),110. 字符串接龙(卡码网),105. 有向图的完全联通(卡码网)
深度优先·图论·广度优先·广搜·岛屿的周长·字符串接龙·有向图的完全联通
zyd09152 天前
代码随想录Day50:图论(图论理论、深度搜索理论、所有可达路径、广度搜索理论)
java·数据结构·算法·leetcode·图论
啊阿狸不会拉杆2 天前
《算法导论》第 22 章 - 基本的图算法
c++·算法·排序算法·图论·拓扑学
James. 常德 student2 天前
leetcode-hot-100 (图论)
算法·leetcode·图论
KarrySmile2 天前
Day51--图论--99. 岛屿数量(卡码网),100. 岛屿的最大面积(卡码网)
图论·深度优先搜索·广度优先搜索·深搜·广搜·岛屿数量·岛屿的最大面积
黑色的山岗在沉睡7 天前
【无标题】
数据结构·c++·算法·图论