前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >leetcode刷题(128)——1575. 统计所有可行路径,动态规划解法

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

作者头像
老马的编程之旅
发布2022-11-16 13:30:49
2450
发布2022-11-16 13:30:49
举报
文章被收录于专栏:深入理解Android深入理解Android

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

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

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

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

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

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

示例 1:

代码语言:javascript
复制
输入: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:

代码语言:javascript
复制
输入: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:

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

提示:

2 <= locations.length <= 100

1 <= locationsi <= 109

所有 locations 中的整数 互不相同 。

0 <= start, finish < locations.length

1 <= fuel <= 200

动态规划

DFS 方法签名设计:

代码语言:javascript
复制
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

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

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

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

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

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

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

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

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

fi = fi + fk

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

从状态转移方程可以发现,在计算 fi 的时候依赖于 fk。

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

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

代码语言:javascript
复制
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 函数签名如何设计即可。

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

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态规划
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档