【算法】动态规划 ⑥ ( 骑士的最短路径 II | 问题分析 | 代码示例 )

news2024/10/6 6:53:04

文章目录

  • 一、问题分析
  • 二、代码示例


骑士的最短路径 II :


在 国际象棋 中 , 骑士 类似 与 象棋 中的 马 , 走 " 日 " 字 格子 ;

骑士有 8 种走法 : " 日 " 字 格子 , 参考 百度百科

  • 左走一格向前走两格
  • 左走一格向后走两格
  • 左走两格向前走一格
  • 左走两格向后走一格
  • 右走一格向前走两格
  • 右走一格向后走两格
  • 右走两格向前走一格
  • 右走两格向后走一格

下图是 骑士 的走法 , 黑色是 骑士的初始位置 ( 0 , 0 ) , 绿色 和 红色 是 骑士 可以走的 下一步位置 ;
在这里插入图片描述


给定一个二维坐标 , 在该坐标系中 , 骑士只能走 上图中 右边 红色的四个方向的步骤 , 计算从 左上角 到 右下角 的最短路径数 ;





一、问题分析



如果 骑士 可以走 8 个方向 ,

  • 那么需要 使用 BFS 宽度优先搜索 算法 ;
  • 此时 不能使用 动态规划解决上述问题 , 如果 可以走 8 个方向 , 那么路径就可以反复 , 会出现 循环依赖的情况 ;

如果 骑士 只能走右边的 4 个方向 , 没有循环依赖 , 则可以使用动态规划 , 解决上述问题 ;


如果 骑士 只能走 右侧的 四个方向 , 也就是

  • 从 黑点 走到 红点 1 , 纵坐标方向上 i 减少 2 行 , 横坐标方向上 j 增加 1 列 ;
  • 从 黑点 走到 红点 2 , 纵坐标方向上 i 减少 1 行 , 横坐标方向上 j 增加 2 列 ;
  • 从 黑点 走到 红点 3 , 纵坐标方向上 i 增加 1 行 , 横坐标方向上 j 增加 2 列 ;
  • 从 黑点 走到 红点 4 , 纵坐标方向上 i 增加 2 行 , 横坐标方向上 j 增加 1 列 ;

在这里插入图片描述

那么 如果当前位置是 ( i , j ) , 那么当前位置的 最短路径 是 dp[i][j] , 那么该点的 最短路径 依赖于 如下几个点的最短路径 :

  • ( i + 2 , j - 1 ) , 对应 从 黑点 走到 红点 1 , 纵坐标方向上 i 减少 2 行 , 横坐标方向上 j 增加 1 列 ;
  • ( i + 1 , j - 2 ) , 对应 从 黑点 走到 红点 2 , 纵坐标方向上 i 减少 1 行 , 横坐标方向上 j 增加 2 列 ;
  • ( i - 1 , j - 2 ) , 对应 从 黑点 走到 红点 3 , 纵坐标方向上 i 增加 1 行 , 横坐标方向上 j 增加 2 列 ;
  • ( i - 2 , j - 1 ) , 对应 从 黑点 走到 红点 4 , 纵坐标方向上 i 增加 2 行 , 横坐标方向上 j 增加 1 列 ;

初始化状态值时 , dp[i][j] 代表了从 起始点 ( 0 , 0 ) 位置 跳转到 ( i , j ) 位置的 最短路径数 ;

该算法求的是 最短路径数 , 初始化 状态 值 时 , 不能初始化为 0 , 这里 初始化为 Integer.MAX_VALUE 值 , 如果值为 Integer.MAX_VALUE 说明该点走不到 ;

如果 算法求的是 方案数 , 则初始化状态值时 , 可以初始化为 0 ;





二、代码示例



代码示例 :

class Solution {

    // 根据骑士只能向右的四个方向 , 走到 (i, j) 点的最短路径, 需要依赖
    // ( i + 2 , j - 1 )
    // ( i + 1 , j - 2 )
    // ( i - 1 , j - 2 )
    // ( i - 2 , j - 1 )
    // 四个点的最短路径, 将上述累加值保存到数组中, 用于快速找到依赖点
    public static int[] deltaX = {2, 1, -1, -2};
    public static int[] deltaY = {-1, -2, -2, -1};

    public int shortestPath2(int[][] obstacleGrid) {
        // 验证函数参数
        if (obstacleGrid == null || obstacleGrid.length == 0) {
            return 0;
        }

        // 1. 动态规划状态 State
        // dp[i][j] 表示 从 (0, 0) 位置出发 , 到 (i, j) 位置的方案总数 ;
        int m = obstacleGrid.length, n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];

        // 2. 动态规划初始化 Initialize
        // 还没开始跳, 此时先将所有的点的状态值设置为 Integer.MAX_VALUE
        // 含义是 所有的点 都无法跳到 , 需要跳无数次才能跳到
        // 但是 (0, 0) 点除外, 其本身跳到本身路径数为 0
        for (int i = 0; i < m ; i++) {
            for (int j = 0; j < n; j++) {
                dp[i][j] = Integer.MAX_VALUE;
            }
        }
        dp[0][0] = 0;

        // 3. 动态规划方程 Function
        // 运动时 , 只能向 右侧的 四个日字方向走
        // ① 纵坐标方向上 i 减少 2 行 , 横坐标方向上 j 增加 1 列 ;
        // ② 纵坐标方向上 i 减少 1 行 , 横坐标方向上 j 增加 2 列 ;
        // ③ 纵坐标方向上 i 增加 1 行 , 横坐标方向上 j 增加 2 列 ;
        // ④ 纵坐标方向上 i 增加 2 行 , 横坐标方向上 j 增加 1 列 ;
        // 从这四个方向中 , 找出路径最小的方向即可
        // 如果遇到障碍物 , 则需要 continue 跳过本次计算 , 继续执行下一次计算 ;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 遇到障碍物 , 跳过
                if (obstacleGrid[i][j] == 1) {
                    continue;
                }

                // 遍历依赖的四个方向
                for (int d = 0; d < 4; d++) {
                    int x = i + deltaX[d];
                    int y = j + deltaY[d];

                    // 判断 x, y 是否超出边界
                    if (x < 0 || x >= n || y < 0 || y >= m) {
                        continue;
                    }

                    // 判断当前位置是否可达, 如果为无穷大 , 说明不可达
                    if (dp[x][y] == Integer.MAX_VALUE) {
                        continue;
                    }

                    // 取当前依赖路径的最小值作为最终的 最小路径数
                    dp[i][j] = Math.min(dp[i][j], dp[x][y] + 1);
                }
            }
        }

        // 4. 动态规划答案 Answer
        if (dp[m - 1][n - 1] == Integer.MAX_VALUE) {
            System.out.println("终点不可达");
            return -1;
        }
        return dp[m - 1][n - 1];
    }

    public static void main(String[] args) {
        // 1 的位置是障碍物
        int[][] obstacleGrid = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}};
        int result = new Solution().shortestPath2(obstacleGrid);
        System.out.println("最短路径数为 " + result);
    }
}

执行结果 :

最短路径数为 2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/81062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jackson注解自定义数据脱敏策略

Jackson注解自定义数据脱敏策略1.前言2.脱敏注解3.定义好一套需要脱敏的规则4.自定义JSON序列化5.在实体类上标注对应的脱敏规则5.写一个接口进行测试1.前言 有时候&#xff0c;我们返回给前端的数据需要脱敏&#xff0c;避免用户信息被泄漏&#xff0c;就像你点外卖一样&…

node.js安装+卸载,npm+cnpm安装+卸载 vue安装+卸载

node.js安装卸载&#xff0c;npmcnpm安装卸载 vue安装卸载 使用指令整理&#xff1a; #获取node.js版本号&#xff08;验证电脑是否安装&#xff09; node -v #node.js官网地址 #https://nodejs.org/en/ #获取npm版本号&#xff08;npm:Nodejs软件包管理工具)&#xff08;验证…

unix网络编程(四) 线程池并发服务器

线程池并发服务器概念线程池和任务队列任务队列线程池操作线程池的函数初始化线程池销毁线程池向线程池添加任务任务的回调函数测试概念 线程池是一个抽象概念&#xff0c;可以简单的认为若干线程在一起运行&#xff0c;线程不退出&#xff0c;等待有任务处理。 为什么要有线程…

通过选择集获取元素

通过使用内置对象document上的getElementsByTagName方法来获取页面上的某一种标签&#xff0c;获取的是一个选择集&#xff0c;不是数组&#xff0c;但是可以用下标的方式操作选择集里面的标签元素 <!DOCTYPE html> <html lang"en"> <head><me…

Javaweb安全——Weblogic反序列化漏洞(一)

从原生反序列化过程开始谈起。 原生反序列化 序列化就是把对象转换成字节流&#xff0c;便于保存在内存、文件、数据库中&#xff1b;反序列化即逆过程&#xff0c;由字节流还原成对象。 大致是这么一个过程&#xff0c;简单画了个图&#xff1a; 测试类如下&#xff1a; p…

spring mvc——@RequestMapping注解的作用

RequestMapping注解 1、RequestMapping注解的功能 从注解名称上我们可以看到&#xff0c;RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来&#xff0c;建立映射关系。 SpringMVC 接收到指定的请求&#xff0c;就会来找到在映射关系中对应的控制器方法来处理…

从源码编译linux内核并运行一个最小的busybox文件系统

从源码编译linux内核并运行一个最小的busybox文件系统 环境基础&#xff1a; 开发环境&#xff1a;ubuntu 18.04 linux源码版本&#xff1a;linux-4.9.229 busybox源码版本&#xff1a;busybox-1.30.0 qemu-system-x86_64版本&#xff1a;2.0.0 这篇文章将按照如下4个步骤来…

【hexo系列】01.hexo环境搭建及github.io搭建

文章目录基础环境要求安装hexohexo初体验创建hexo工程初体验创建自己的第一篇笔记推送到github网站新建github.io推送到github推送到github(ssh方式 免密)参考资料基础环境要求 检测Node.js是否安装成功&#xff0c;在命令行中输入 node -v 检测npm是否安装成功&#xff0c;在…

机器学习中的数学原理——多重回归算法

这个专栏主要是用来分享一下我在机器学习中的学习笔记及一些感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎私信或者评论区留言&#xff01;这一篇就更新一下《白话机器学习中的数学——多重回归算法》&#xff01; 目录 一、什么是多重回归 二、案…

物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块

一、目的 这一节我们学习如何使用我们的ESP32开发板来通过SPI接口控制Micro SD卡TF卡模块。 二、环境 ESP32 SPI接口控制Micro SD卡TF卡模块 Thonny IDE 几根杜邦线 接线方法&#xff1a; Soft SPI接线说明 # 接线说明: # MISO -> GPTO13 # MOSI -> GPIO12 # SCK …

[附源码]Python计算机毕业设计SSM基于的楼盘销售系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

SpringCloud入门实战-Ribbon

SpringCloud入门实战-Ribbon使用 原创目录概述需求&#xff1a;设计思路实现思路分析1.Ribbon原理2.Ribbon负载均衡策略参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a bet…

计算机软技术,如何画好一张架构图?

什么是架构图&#xff1f; 如何画好一张架构图&#xff0c;要做好这件事情首先要回答的就是什么是架构图。我们日常工作中经常能看到各种各样的架构图&#xff0c;而且经常会发现大家对架构图的理解各有侧重。深入追究到这个问题&#xff0c;可能一下子还很难有一个具象的定义…

动态路由协议RIP

数据来源 一、动态路由 基于某种协议实现 1&#xff09;动态路由拓补图 2&#xff09;动态路由特点 减少了管理任务占用了网络带宽 3&#xff09;动态路由协议概述 路由器之间用来交换信息的语言 4&#xff09;度量值 跳数、带宽、负载、时延、可靠性、成本 跳数&#xff1a…

JavaScript数据结构【数组---for...of循环迭代】

继for循环&#xff0c;和forEach方法迭代数组后&#xff0c;要想迭代数组的值还可以用for...of循环 使用&#xff1a; // for...of循环示例 let array [1, 2, 3] for (let key of array) {console.log(key); } /* 输出&#xff1a;123 */ 可以看到&#xff1a;使用for...of…

嵌入式介绍与应用

嵌入式介绍与应用1 概念桌面对比2 特点3 发展历史3.1 计算机发展3.2 嵌入式发展4 开发能力要求5 应用6 规模参考1 概念 嵌入式系统由硬件和软件组成。是能够独立进行运作的器件。其软件内容只包括软件运行环境及其操作系统。硬件内容包括信号处理器、存储器、通信模块等在内的…

构建过程:从源码到dist文件

问题 有没有好奇过&#xff0c;自己写的前端代码是怎么变成上线可用的代码的&#xff1f; 前言 目前实现从源码到可用的静态文件&#xff0c;我们都是借助打包工具实现的&#xff0c;目前用的比较多的是webpack、rollup、vite..., 那么以上问题也可以描述为“构建工具是如何…

ChatGPT教程之 03 ChatGPT 中构建 Python 解释器

这个故事的灵感来自于一个类似的故事,在 ChatGPT 中构建虚拟机。我印象深刻并决定尝试类似的东西,但这次不是 Linux 命令行工具,而是让 ChatGPT 成为我们的 Python 解释器。 这是初始化 ChatGPT 的初始命令: I want you to act as a Python interpreter. I will type com…

<<两万字通关Java IO流>>

✨✨hello&#xff0c;愿意点进来的小伙伴们&#xff0c;你们好呐&#xff01; &#x1f43b;&#x1f43b;系列专栏&#xff1a;【JavaEE】 &#x1f432;&#x1f432;本篇内容&#xff1a;详解Java IO流 &#x1f42f;&#x1f42f;作者简介:一名现大二的三非编程小白&#…

python----函数、文件、以及高级特性

文章目录前言一、函数的基本概念二、文件OS模块json模块高级特性生成式生成器闭包装饰器前言 一、函数的基本概念 **全局变量&#xff1a;**在函数外边定义的变量&#xff0c;全局生效 **局部变量&#xff1a;**在函数里边定义的变量&#xff0c;局部生效 如果要在函数中修改全…