leetcode刷题(128)——1575. 统计所有可行路径,动态规划解法

news2024/11/26 14:39:56

leetcode刷题(127)——1575. 统计所有可行路径,DFS解法

给你一个 互不相同 的整数数组,其中 locations[i] 表示第 i 个城市的位置。同时给你 start,finish 和 fuel 分别表示出发城市、目的地城市和你初始拥有的汽油总量

每一步中,如果你在城市 i ,你可以选择任意一个城市 j ,满足 j != i 且 0 <= j < locations.length ,并移动到城市 j 。从城市 i 移动到 j 消耗的汽油量为 |locations[i] - locations[j]|,|x| 表示 x 的绝对值。

请注意, fuel 任何时刻都 不能 为负,且你 可以 经过任意城市超过一次(包括 start 和 finish )。

请你返回从 start 到 finish 所有可能路径的数目。

由于答案可能很大, 请将它对 10^9 + 7 取余后返回。

示例 1:

输入:locations = [2,3,6,8,4], start = 1, finish = 3, fuel = 5
输出:4
解释:以下为所有可能路径,每一条都用了 5 单位的汽油:
1 -> 3
1 -> 2 -> 3
1 -> 4 -> 3
1 -> 4 -> 2 -> 3

示例 2:

输入:locations = [4,3,1], start = 1, finish = 0, fuel = 6
输出:5
解释:以下为所有可能的路径:
1 -> 0,使用汽油量为 fuel = 1
1 -> 2 -> 0,使用汽油量为 fuel = 5
1 -> 2 -> 1 -> 0,使用汽油量为 fuel = 5
1 -> 0 -> 1 -> 0,使用汽油量为 fuel = 3
1 -> 0 -> 1 -> 0 -> 1 -> 0,使用汽油量为 fuel = 5

示例 3:

输入:locations = [5,2,1], start = 0, finish = 2, fuel = 3
输出:0
解释:没有办法只用 3 单位的汽油从 0 到达 2 。因为最短路径需要 4 单位的汽油。

提示:

2 <= locations.length <= 100
1 <= locations[i] <= 109
所有 locations 中的整数 互不相同 。
0 <= start, finish < locations.length
1 <= fuel <= 200

动态规划

DFS 方法签名设计:

int dfs(int[] ls, int u, int end, int fuel) {}

其中,ls 参数和 end 参数分别代表源输入的 locations 和 finish,在整个 DFS 过程都不会变化,属于不变参数。

而 u 参数和 fuel 参数则是代表了 DFS 过程中的当前位置和当前油量,属于变化参数。

因此我们可以定一个 f[][] 二维数组,来分别表示两个可变参数。

第一维代表当前位置(对应 locations 数组的下标),第二维代表当前剩余油量。

二维数组中存储的就是我们的 DFS 方法的返回值(路径数量)。

同时结合题意,不难得知维度的取值范围:

第一维的取值范围为 [0,locations.length)
第二维的取值范围为[0,fuel]

做完这一步的”翻译“工作,我们就得到了「动态规划」的「状态定义」了。

f[i][j] 代表从位置 出发,当前剩余油量为 的前提下,到达目的地的路径数量。

不知道你是否发现,这个「状态定义」和我们「记忆化搜索」中的缓存器的定义是一致的。

接下来我们要从 DFS 中”翻译“出「状态转移方程」。

所谓的「状态转移方程」其实就是指如何从一个状态转移到另外一个状态。

而我们的 DFS 主逻辑就是完成这个转移的。

DFS 中的主逻辑很简单:枚举所有的位置,看从当前位置 出发,可以到达的位置有哪些。

于是我们很容易就可以得出状态转移方程:

f[i][fuel] = f[i][fuel] + f[k][fuel-need]

k 代表计算位置 i 油量 fuel 的状态时枚举的「下一位置」,need 代表从 i 到达 k 需要的油量。

从状态转移方程可以发现,在计算 f[i][fuel] 的时候依赖于 f[k][fuel-need]。

其中 i 和 k 并无严格的大小关系,而 fuel 和 fuel - need 具有严格的大小关系(fuel>=fuel-need)。

因此我们需要先从小到大枚举油量这一维。

class Solution {
    int mod = 1000000007;
    public int countRoutes(int[] ls, int start, int end, int fuel) {
        int n = ls.length;

        // f[i][j] 代表从位置 i 出发,当前油量为 j 时,到达目的地的路径数
        int[][] f = new int[n][fuel + 1];
        
        // 对于本身位置就在目的地的状态,路径数为 1
        for (int i = 0; i <= fuel; i++) f[end][i] = 1;

        // 从状态转移方程可以发现 f[i][fuel]=f[i][fuel]+f[k][fuel-need]
        // 在计算 f[i][fuel] 的时候依赖于 f[k][fuel-need]
        // 其中 i 和 k 并无严格的大小关系
        // 而 fuel 和 fuel-need 具有严格大小关系:fuel >= fuel-need
        // 因此需要先从小到大枚举油量
        for (int cur = 0; cur <= fuel; cur++) {
            for (int i = 0; i < n; i++) {
                for (int k = 0; k < n; k++) {
                    if (i != k) {
                        int need = Math.abs(ls[i] - ls[k]);
                        if (cur >= need) {
                            f[i][cur] += f[k][cur-need];
                            f[i][cur] %= mod;
                        }
                    }
                }
            }
        }
        return f[start][fuel];
    }
}

在这里插入图片描述
至此,我们只利用 DFS 的方法签名与主逻辑,就写出了「动态规划」解法。

我再帮你来总结一下这个过程:

  1. 从 DFS 方法签名出发。分析哪些入参是可变的,将其作为 DP 数组的维度;将返回值作为 DP 数组的存储值。

  2. 从 DFS 的主逻辑可以抽象中单个状态的计算方法。

其中第一点对应了「动态规划」的「状态定义」,第二点对应了「动态规划」的「状态方程转移」。

到目前为止,我们已经掌握了两种求解「动态规划」问题的方法:

  1. 根据经验猜一个「状态定义」,然后根据「状态定义」去推导一个「状态转移方程」。

  2. 先写一个「记忆化搜索」解法,再将「记忆化搜索」改写成「动态规划」。

能够去猜「状态定义」或者使用「记忆化搜索」求解,都有一个大前提:问题本身具有无效性。

由于「动态规划」的状态定义猜测,是一门很讲求经验的技能。

因此对于那些你接触过的模型,我建议你使用第一种方式;

如果遇到一道你从来没接触过的题目时,我建议你先想想「记忆化搜索」该如何实现,然后反推出「动态规划」。

这里说的想想「记忆化搜索」该如何实现,不需要真正动手实现一个「记忆化搜索」解法,而只需要想清楚,如果使用「记忆化搜索」的话,我的 DFS 函数签名如何设计即可。

当搞清楚「记忆化搜索」的函数签名设计之后,「状态定义」部分基本就已经出来了,之后的「状态转移方程」就还是一样的分析方法。

当然,如果你觉得「记忆化搜索」更好实现的话,大可直接使用「记忆化搜索」求解,不一定需要将其转化为「动态规划」。

因为由「记忆化搜索」直接转过来的「动态规划」,两者复杂度是一样的。而且通常「记忆化搜索」的实现难度通常要低很多。

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

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

相关文章

【CSS】CSS字体样式【CSS基础知识详解】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 本文章收录于专栏 【CSS】 【CSS专栏】已发布文章 &#x1f4c1;【CSS基础认知】 &#x1f4c1;【CSS选择器全解指南】 本文目录【CS…

物联网感知-光纤光栅传感器技术

一、光纤光栅传感技术 光纤光栅是利用光纤材料的光敏性&#xff0c;通过紫外光曝光的方法将入射光相干场图样写入纤芯&#xff0c;将周期性微扰作用于光纤纤芯&#xff0c;在纤芯内产生沿纤芯轴向的折射率周期性变化&#xff0c;从而形成永久性空间的相位光栅&#xff0c;其作用…

MySQL数据库的基本操作及存储引擎的使用

大家好呀&#xff01;我是猿童学&#x1f435;&#xff0c;最近在学习Mysql数据库&#xff0c;给初学者分享一些知识&#xff0c;也是学习的总结&#xff0c;关注我将会不断地更新数据库知识&#xff0c;也欢迎大家指点一二&#x1f339;。 目录 一、常用的MySQL语句 二、创建…

使用ThinkMusic网站源码配合cpolar,发布本地音乐网站

1、前言 在我们的日常生活中&#xff0c;音乐已经成为不可或缺的要素之一&#xff0c;听几首喜欢的音乐&#xff0c;能让原本糟糕的心情变得好起来。虽然现在使用电脑或移动电子设备听歌都很方便&#xff0c;但难免受到诸多会员或VIP限制&#xff0c;难免让我们回想起音乐网站…

【JavaScript】常用内置对象——数组(Array)对象

文章目录什么是数组创建数组访问数组数组常用方法和属性投票传送门什么是数组 数组&#xff08;Array&#xff09;是最基本的集合类型&#xff0c;由于JavaScript是弱类型语言&#xff0c;因此JavaScript的数组和大多数语言的数组有所区别。在大多数语言中&#xff0c;当声明一…

ubuntu 20.04 qemu u-boot-2022.10 开发环境搭建

开发环境 ubuntu 20.04 VMware Workstation Pro 16 基于qemu&#xff08;模拟器&#xff09;&#xff0c;vexpress-a9 平台 搭建 u-boot-2022.10 (当前最新版本&#xff09; 准备工作 u-boot下载&#xff0c;下载最新稳定版本&#xff0c;当前为 u-boot-2022.10&#xff0…

代码随想录49——动态规划:121买卖股票的最佳时机、122买卖股票的最佳时机II

文章目录1.121买卖股票的最佳时机1.1.题目1.2.解答1.2.1.贪心算法1.2.2.动态规划2.122买卖股票的最佳时机II2.1.题目2.2.解答1.121买卖股票的最佳时机 参考&#xff1a;代码随想录&#xff0c;121买卖股票的最佳时机&#xff1b;力扣题目链接 1.1.题目 1.2.解答 1.2.1.贪心算…

第七节:类和对象【一】【java】

目录 &#x1f9fe;1. 面向对象的初步认知 1.1 什么是面向对象 1.2 面向对象与面向过程 &#x1f4d5;2. 类定义和使用 2.1 简单认识类 2.2 类的定义格式 2.3 课堂练习 &#x1f392;3. 类的实例化 3.1 什么是实例化 3.2 类和对象的说明 3.3练习 &#x1f9fe;1. 面…

Hbase2.4.11安装

Hbase2.4.11安装 文章目录Hbase2.4.11安装一、前期准备二、安装三、配置文件(一)添加环境变量&#xff08;二&#xff09;修改hbase配置文件1.修改hbase-env.sh 中内容2.在hbase-site.xml中添加以下内容3. 编辑regionservers四、分发文件到Hadoop2、Hadoop3中一、前期准备 hba…

SpringBoot+Vue实现前后端分离的学生选课系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

浏览器无痕模式有什么作用,手机浏览器开启无痕模式的方法

在我们的手机基本上都安装了浏览器&#xff0c;当我们在上网过程中&#xff0c;不想浏览记录被留下&#xff0c;那么开启无痕模式是非常有必要的。那么&#xff0c;浏览器的无痕模式有什么作用&#xff0c;手机浏览器如何开启无痕模式呢&#xff1f;下面教大家如何在手机浏览器…

HECTF2022

今年是第三次参加HECTF&#xff0c;成绩不是很好wp随便看看就好了 文章目录Misc咦~小鲨鱼来喽舞者的秘密你把我flag藏哪去了?来玩捉迷藏呀我的手要不行辣2022HECTF调查问卷Crypto流动的音符matrixezrsamixtureReverseapk贝斯helloiosrunWeb迷路的小狮擎天注Pwn真签到Misc 咦~…

马斯克的这波神操作,让我意识到保持写代码的能力有多重要

作为一个在IT行业摸爬滚打了多年的老油条&#xff0c;我是越来越看不懂现在的互联网行业了。 至少曾经我听过太多人吐槽写代码的永远干不过写PPT的&#xff0c;并且在现实工作中验证过也确实如此&#xff0c;但是老马的这一波骚操作&#xff0c;让推特工程师打印出最近30-60天…

《这!就是街舞》,好综艺还是好生意?

01.始于热爱&#xff0c;火于流量&#xff0c;不止综艺&#xff0c;这就是街舞 “每个人生而不同&#xff0c;不需要被包裹成别人需要的面孔。我就是我&#xff0c;既不傲慢&#xff0c;也不卑微。” ——李承弦 这段来自综艺《这&#xff01;就是街舞》第五季中节目对于李承…

ES6的Promise详解

文章目录前言一、Promise的概念二、使用Promise创建 PromisePromise 常用方法Promise.prototype.then()Promise.prototype.catch()all()链式调用前言 本篇文章主要介绍了ES6语法中的Promise对象的使用详解,promise对象是JS进阶学习中的重要知识点&#xff0c; 如果本文对你有所…

JavaScipt基础(持续更新三)

JavaScipt基础 JavaScipt基础 九、对象&#xff08;Object&#xff09; 9.1什么是对象 9.2JavaScript中的对象 9.3如何得到一个对象 9.4this的指向 9.5对象的使用 十、标准库对象&#xff08;内置对象&#xff09; 10.1Math对象 10.1.1常用属性和方法 10.1.2案例 1…

8年测试经验,简单易懂的讲解一下什么是自动化测试?

自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常&#xff0c;在设计了测试用例并通过评审之后&#xff0c;由测试人员根据测试用例中描述的规程一步步执行测试&#xff0c;得到实际结果与期望结果的比较。在此过程中&#xff0c;为了节省人力、时间或硬件资…

第九期|不是吧,我在社交媒体的照片也会被网络爬虫?

顶象防御云业务安全情报中心监测到&#xff0c;某社交媒体平台遭遇持续性的恶意爬虫盗取。被批量盗取用户信息和原创内容&#xff0c;经分类梳理和初步加工后&#xff0c;被黑灰产转售给竞争对手或直接用于恶意营销。由此不仅给社交媒体平台的数字资产带来直接损失&#xff0c;…

人工智能--机器学习概述、motplotlib的使用-折线图、散点图、柱状图、饼图

机器学习 步骤&#xff1a; 获取数据–数据基本处理–特征工程–机器学习&#xff08;算法&#xff09;–模型评估与调优 人工智能三要素&#xff1a;数据、算法、计算力 CPU 控制单元多&#xff0c;计算单元少—更适合IO密集型任务 GPU计算单元多----更适合计算密集型任务 …

linux环境部署

linux安装go环境 1、下载go的安装包 Golang官网下载地址&#xff1a;https://golang.org/dl/ 2、包版本&#xff1a;go1.19.3.linux-arm64.tar.gz cd /usr/local tar -zxvf go1.19.3.linux-arm64.tar.gz 3、将/usr/local/go/bin添加到环境变量中 vim /etc/profile #在最后一行…