Skip to content

Commit be106f5

Browse files
author
lucifer
committed
feat: lcp20
1 parent c44891f commit be106f5

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ leetcode 题解,记录自己的 leetcode 解题之路。
389389

390390
以下是我列举的经典题目(带 91 字样的表示出自 **91 天学算法**活动):
391391

392+
- [LCP 20. 快速公交](./problems/lcp20.meChtZ.md) 🆕
393+
392394
- [0004. 寻找两个正序数组的中位数](./problems/4.median-of-two-sorted-arrays.md)
393395
- [0023. 合并 K 个升序链表](./problems/23.merge-k-sorted-lists.md)
394396
- [0025. K 个一组翻转链表](./problems/25.reverse-nodes-in-k-groups.md)

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225

226226

227227
* [第六章 - 高频考题(困难)](collections/hard.md)
228+
* [LCP 20. 快速公交](problems/lcp20.meChtZ.md) 🆕
228229
* [0004. 寻找两个正序数组的中位数](problems/4.median-of-two-sorted-arrays.md)
229230
* [0023. 合并K个升序链表](problems/23.merge-k-sorted-lists.md)
230231
* [0025. K 个一组翻转链表](problems/25.reverse-nodes-in-k-groups.md)

problems/lcp20.meChtZ.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
![](https://tva1.sinaimg.cn/large/008eGmZEly1gn7w8lgdi2j30v80iggmi.jpg)
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 == 0return 0
133+
            if pos == 1return 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+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)