|
| 1 | +## 题目地址(LCP 20. 快速公交) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/meChtZ/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +小扣打算去秋日市集,由于游客较多,小扣的移动速度受到了人流影响: |
| 9 | +
|
| 10 | +小扣从 x 号站点移动至 x + 1 号站点需要花费的时间为 inc; |
| 11 | +小扣从 x 号站点移动至 x - 1 号站点需要花费的时间为 dec。 |
| 12 | +
|
| 13 | +现有 m 辆公交车,编号为 0 到 m-1。小扣也可以通过搭乘编号为 i 的公交车,从 x 号站点移动至 jump[i]*x 号站点,耗时仅为 cost[i]。小扣可以搭乘任意编号的公交车且搭乘公交次数不限。 |
| 14 | +
|
| 15 | +假定小扣起始站点记作 0,秋日市集站点记作 target,请返回小扣抵达秋日市集最少需要花费多少时间。由于数字较大,最终答案需要对 1000000007 (1e9 + 7) 取模。 |
| 16 | +
|
| 17 | +注意:小扣可在移动过程中到达编号大于 target 的站点。 |
| 18 | +
|
| 19 | +示例 1: |
| 20 | +
|
| 21 | +输入:target = 31, inc = 5, dec = 3, jump = [6], cost = [10] |
| 22 | +
|
| 23 | +输出:33 |
| 24 | +
|
| 25 | +解释: |
| 26 | +小扣步行至 1 号站点,花费时间为 5; |
| 27 | +小扣从 1 号站台搭乘 0 号公交至 6 * 1 = 6 站台,花费时间为 10; |
| 28 | +小扣从 6 号站台步行至 5 号站台,花费时间为 3; |
| 29 | +小扣从 5 号站台搭乘 0 号公交至 6 * 5 = 30 站台,花费时间为 10; |
| 30 | +小扣从 30 号站台步行至 31 号站台,花费时间为 5; |
| 31 | +最终小扣花费总时间为 33。 |
| 32 | +
|
| 33 | +示例 2: |
| 34 | +
|
| 35 | +输入:target = 612, inc = 4, dec = 5, jump = [3,6,8,11,5,10,4], cost = [4,7,6,3,7,6,4] |
| 36 | +
|
| 37 | +输出:26 |
| 38 | +
|
| 39 | +解释: |
| 40 | +小扣步行至 1 号站点,花费时间为 4; |
| 41 | +小扣从 1 号站台搭乘 0 号公交至 3 * 1 = 3 站台,花费时间为 4; |
| 42 | +小扣从 3 号站台搭乘 3 号公交至 11 * 3 = 33 站台,花费时间为 3; |
| 43 | +小扣从 33 号站台步行至 34 站台,花费时间为 4; |
| 44 | +小扣从 34 号站台搭乘 0 号公交至 3 * 34 = 102 站台,花费时间为 4; |
| 45 | +小扣从 102 号站台搭乘 1 号公交至 6 * 102 = 612 站台,花费时间为 7; |
| 46 | +最终小扣花费总时间为 26。 |
| 47 | +
|
| 48 | +提示: |
| 49 | +
|
| 50 | +1 <= target <= 10^9 |
| 51 | +1 <= jump.length, cost.length <= 10 |
| 52 | +2 <= jump[i] <= 10^6 |
| 53 | +1 <= inc, dec, cost[i] <= 10^6 |
| 54 | +``` |
| 55 | + |
| 56 | +## 前置知识 |
| 57 | + |
| 58 | +- 递归 |
| 59 | +- [回溯](https://github.com/azl397985856/leetcode/blob/master/thinkings/backtrack.md) |
| 60 | +- 动态规划 |
| 61 | + |
| 62 | +## 公司 |
| 63 | + |
| 64 | +- 暂无 |
| 65 | + |
| 66 | +## 思路 |
| 67 | + |
| 68 | +这道题我们可以直接模拟所有可能性,找出最小的即可。 |
| 69 | + |
| 70 | +那么如何模拟呢?这里的模拟思路其实和回溯是一样的。我们可以使用递归控制一个变量,递归函数内部控制另外一个变量。 |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +具体来说,我们可以用递归控制当前位置这一变量,递归函数内部循环遍历 jumps。自然语言表达就是**对于每一个位置 pos,我们都可以选择我先走一步(之后怎么走不管)到终点或者先乘坐一个公交车(之后怎么走不管)到终点**。 |
| 75 | + |
| 76 | +核心代码: |
| 77 | + |
| 78 | +``` |
| 79 | +def dfs(pos): |
| 80 | + if pos === target: return 0 |
| 81 | + if pos > target: return float('inf') |
| 82 | + ans = (target - pos) * inc |
| 83 | + for jump in jumps: |
| 84 | + ans = min(ans, 乘坐本次公交的花费) |
| 85 | + return ans |
| 86 | +dfs(0) |
| 87 | +``` |
| 88 | + |
| 89 | +上面代码大体思路没有问题。 只是少考虑了一个点**小扣可在移动过程中到达编号大于 target 的站点。** 如果加上这个条件,我们递归到 pos 大于 target 的时候**并不能**认为其是一个非法解。 |
| 90 | + |
| 91 | +实际上,我们也可采取逆向思维,即从 target 出发返回 0,这无疑和从 0 出发到 target 的花费是等价的, 但是这样可以简化逻辑。为什么可以简化逻辑呢?是不需要考虑大于 target 了么?当然不是,那样会错过正解。我举个例子你就懂了。 比如: |
| 92 | + |
| 93 | +``` |
| 94 | +target = 5 |
| 95 | +jumps = [3] |
| 96 | +``` |
| 97 | + |
| 98 | +当前的位置 pos = 2,选择乘坐公交车会到达 2 \* 3 = 6 。这样往回走一站就可以到达 target 了。如果采用逆向思维如何考虑这一点呢? |
| 99 | + |
| 100 | +逆向思维是从 5 开始。先将 5 / 3 (整数除) 得到 1,余数是 2。 这意味着有两种到达此位置的方式: |
| 101 | + |
| 102 | +- 先想办法到 1,再乘坐本次公交到 3(1 \* 3 = 3),然后想办法往前走 2(5 % 3 = 2). |
| 103 | +- 先想办法到 2,再乘坐本次公交到 6(2 \* 3 = 6),然后想办法往后走 1. (3 - 5 % 3 = 1 ) |
| 104 | + |
| 105 | +> 这就考虑到了超过 target 的情况。 |
| 106 | +
|
| 107 | +特殊地,如果可以整除,我们直接乘坐公交车就行了,无需走路 🚶。 |
| 108 | + |
| 109 | +有的同学可能有疑问,为什么不继续下去。 比如: |
| 110 | + |
| 111 | +- 先想办法到 3,再乘坐本次公交到 9(3 \* 3 = 9),然后想办法往后走 1. (3 + 3 - 5 % 3 = 4 ) |
| 112 | +- 。。。 |
| 113 | + |
| 114 | +这是没有必要的,因为这些情况一定都比上面两种情况花费更多。 |
| 115 | + |
| 116 | +## 关键点 |
| 117 | + |
| 118 | +- 逆向思维 |
| 119 | + |
| 120 | +## 代码 |
| 121 | + |
| 122 | +- 语言支持:Python3 |
| 123 | + |
| 124 | +Python3 Code: |
| 125 | + |
| 126 | +```python |
| 127 | + |
| 128 | +class Solution: |
| 129 | + def busRapidTransit(self, target: int, inc: int, dec: int, jumps: List[int], cost: List[int]) -> int: |
| 130 | + @lru_cache(None) |
| 131 | + def dfs(pos): |
| 132 | + if pos == 0: return 0 |
| 133 | + if pos == 1: return inc |
| 134 | + ans = pos * inc |
| 135 | + for i, jump in enumerate(jumps): |
| 136 | + pre_pos, left = pos // jump, pos % jump |
| 137 | + if left == 0: ans = min(ans, cost[i] + dfs(pre_pos)) |
| 138 | + else: ans = min(ans, cost[i] + dfs(pre_pos) + inc * left, cost[i] + dfs(pre_pos + 1) + dec * (jump - left)) |
| 139 | + return ans |
| 140 | + return dfs(target) % 1000000007 |
| 141 | + |
| 142 | +``` |
| 143 | + |
| 144 | +更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。 |
| 145 | + |
| 146 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 147 | + |
| 148 | + |
0 commit comments