Skip to content

Commit 6ff2498

Browse files
committed
feat: leetcode 871
1 parent 2d1ee6d commit 6ff2498

File tree

9 files changed

+245
-3
lines changed

9 files changed

+245
-3
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<p align="center">
44
<!-- TOPICS COUNT START -->
5-
<img src="https://img.shields.io/badge/-进度:418-green" alt="进度:418">
5+
<img src="https://img.shields.io/badge/-进度:419-green" alt="进度:419">
66
<!-- TOPICS COUNT END -->
77
<a href="./assets/docs/TOPICS.md"><img src="https://img.shields.io/badge/-题库目录-blue" alt="题库目录"></a>
88
<a href="./assets/docs/CATEGORIES.md"><img src="https://img.shields.io/badge/-题库分类-red" alt="题库分类"></a>

assets/data/categories.json

+12
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,12 @@
13931393
"path": "./problemset/count-different-palindromic-subsequences/README.md",
13941394
"difficulty": "困难"
13951395
},
1396+
{
1397+
"id": "871",
1398+
"title": "最低加油次数",
1399+
"path": "./problemset/minimum-number-of-refueling-stops/README.md",
1400+
"difficulty": "困难"
1401+
},
13961402
{
13971403
"id": "926",
13981404
"title": "将字符串翻转到单调递增",
@@ -3040,6 +3046,12 @@
30403046
"path": "./problemset/increasing-triplet-subsequence/README.md",
30413047
"difficulty": "中等"
30423048
},
3049+
{
3050+
"id": "871",
3051+
"title": "最低加油次数",
3052+
"path": "./problemset/minimum-number-of-refueling-stops/README.md",
3053+
"difficulty": "困难"
3054+
},
30433055
{
30443056
"id": "942",
30453057
"title": "增减字符串匹配",

assets/data/topics.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -3499,6 +3499,16 @@
34993499
"url": "https://leetcode-cn.com/problems/binary-gap/",
35003500
"path": "./problemset/binary-gap/README.md"
35013501
},
3502+
{
3503+
"id": "871",
3504+
"title": {
3505+
"cn": "最低加油次数",
3506+
"en": "minimum-number-of-refueling-stops"
3507+
},
3508+
"difficulty": "困难",
3509+
"url": "https://leetcode.cn/problems/minimum-number-of-refueling-stops/",
3510+
"path": "./problemset/minimum-number-of-refueling-stops/README.md"
3511+
},
35023512
{
35033513
"id": "875",
35043514
"title": {
@@ -4179,4 +4189,4 @@
41794189
"url": "https://leetcode.cn/problems/JEj789/",
41804190
"path": "./problemset/paint-house/README.md"
41814191
}
4182-
]
4192+
]

assets/docs/CATEGORIES.md

+2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@
265265
| 473. [火柴拼正方形](../../problemset/matchsticks-to-square/README.md) | 中等 |
266266
| 688. [骑士在棋盘上的概率](../../problemset/knight-probability-in-chessboard/README.md) | 中等 |
267267
| 730. [统计不同回文子序列](../../problemset/count-different-palindromic-subsequences/README.md) | 困难 |
268+
| 871. [最低加油次数](../../problemset/minimum-number-of-refueling-stops/README.md) | 困难 |
268269
| 926. [将字符串翻转到单调递增](../../problemset/flip-string-to-monotone-increasing/README.md) | 中等 |
269270
| 1994. [好子集的数目](../../problemset/the-number-of-good-subsets/README.md) | 困难 |
270271
| 2100. [适合打劫银行的日子](../../problemset/find-good-days-to-rob-the-bank/README.md) | 中等 |
@@ -577,6 +578,7 @@
577578
| 300. [最长递增子序列](../../problemset/longest-increasing-subsequence/README.md) | 中等 |
578579
| 330. [按要求补齐数组](../../problemset/patching-array/README.md) | 困难 |
579580
| 334. [递增的三元子序列](../../problemset/increasing-triplet-subsequence/README.md) | 中等 |
581+
| 871. [最低加油次数](../../problemset/minimum-number-of-refueling-stops/README.md) | 困难 |
580582
| 942. [增减字符串匹配](../../problemset/di-string-match/README.md) | 简单 |
581583
| 1663. [具有给定数值的最小字符串](../../problemset/smallest-string-with-a-given-numeric-value/README.md) | 中等 |
582584
| 2038. [如果相邻两个颜色均相同则删除当前颜色](../../problemset/remove-colored-pieces-if-both-neighbors-are-the-same-color/README.md) | 中等 |

assets/docs/TOPICS.md

+2
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@
700700

701701
[868. 二进制间距](../../problemset/binary-gap/README.md)
702702

703+
[871. 最低加油次数](../../problemset/minimum-number-of-refueling-stops/README.md)
704+
703705
[875. 爱吃香蕉的珂珂](../../problemset/koko-eating-bananas/README.md)
704706

705707
[883. 三维形体投影面积](../../problemset/projection-area-of-3d-shapes/README.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# 最低加油次数
2+
3+
> 难度:困难
4+
>
5+
> https://leetcode.cn/problems/minimum-number-of-refueling-stops/
6+
7+
## 题目
8+
9+
汽车从起点出发驶向目的地,该目的地位于出发位置东面 `target` 英里处。
10+
11+
沿途有加油站,每个 `station[i]` 代表一个加油站,它位于出发位置东面 `station[i][0]` 英里处,并且有 `station[i][1]` 升汽油。
12+
13+
假设汽车油箱的容量是无限的,其中最初有 `startFuel` 升燃料。它每行驶 `1` 英里就会用掉 `1` 升汽油。
14+
15+
当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。
16+
17+
为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 `-1`
18+
19+
注意:如果汽车到达加油站时剩余燃料为 `0`,它仍然可以在那里加油。如果汽车到达目的地时剩余燃料为 `0`,仍然认为它已经到达目的地。
20+
21+
### 示例
22+
23+
#### 示例 1:
24+
25+
```
26+
输入:target = 1, startFuel = 1, stations = []
27+
输出:0
28+
解释:我们可以在不加油的情况下到达目的地。
29+
```
30+
31+
#### 示例 2:
32+
33+
```
34+
输入:target = 100, startFuel = 1, stations = [[10,100]]
35+
输出:-1
36+
解释:我们无法抵达目的地,甚至无法到达第一个加油站。
37+
```
38+
39+
#### 示例 3:
40+
41+
```
42+
输入:target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
43+
输出:2
44+
解释:
45+
我们出发时有 10 升燃料。
46+
我们开车来到距起点 10 英里处的加油站,消耗 10 升燃料。将汽油从 0 升加到 60 升。
47+
然后,我们从 10 英里处的加油站开到 60 英里处的加油站(消耗 50 升燃料),
48+
并将汽油从 10 升加到 50 升。然后我们开车抵达目的地。
49+
我们沿途在1两个加油站停靠,所以返回 2 。
50+
```
51+
52+
## 解题
53+
54+
### 动态规划
55+
56+
```ts
57+
/**
58+
* 动态规划
59+
* @desc 时间复杂度 O(N²) 空间复杂度 O(N)
60+
* @param target
61+
* @param startFuel
62+
* @param stations
63+
* @returns
64+
*/
65+
export function minRefuelStops(
66+
target: number,
67+
startFuel: number,
68+
stations: number[][],
69+
): number {
70+
const len = stations.length
71+
const dp = new Array(len + 1).fill(0)
72+
dp[0] = startFuel
73+
74+
for (let i = 0; i < len; i++) {
75+
for (let j = i; j >= 0; j--) {
76+
if (dp[j] >= stations[i][0])
77+
dp[j + 1] = Math.max(dp[j + 1], dp[j] + stations[i][1])
78+
}
79+
}
80+
81+
for (let i = 0; i <= len; i++) {
82+
if (dp[i] >= target)
83+
return i
84+
}
85+
86+
return -1
87+
}
88+
```
89+
90+
### 贪心算法
91+
92+
```ts
93+
/**
94+
* 贪心算法
95+
* @desc 时间复杂度 O(NlogN) 空间复杂度 O(N)
96+
* @param target
97+
* @param startFuel
98+
* @param stations
99+
* @returns
100+
*/
101+
export function minRefuelStops2(
102+
target: number,
103+
startFuel: number,
104+
stations: number[][],
105+
): number {
106+
const pq = new PriorityQueue<number>((a, b) => b - a >= 0)
107+
let ans = 0
108+
let prev = 0
109+
let fuel = startFuel
110+
const len = stations.length
111+
112+
for (let i = 0; i <= len; i++) {
113+
const curr = i < len ? stations[i][0] : target
114+
fuel -= curr - prev
115+
while (fuel < 0 && !pq.isEmpty()) {
116+
fuel += pq.poll()!
117+
ans++
118+
}
119+
120+
if (fuel < 0) return -1
121+
122+
if (i < len) {
123+
pq.offer(stations[i][1])
124+
prev = curr
125+
}
126+
}
127+
return ans
128+
}
129+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { minRefuelStops, minRefuelStops2 } from '.'
3+
4+
describe('最低加油次数', () => {
5+
describe('动态规划', () => testCase(minRefuelStops))
6+
describe('贪心算法', () => testCase(minRefuelStops2))
7+
})
8+
9+
function testCase(fn: (target: number, startFuel: number, stations: number[][]) => number) {
10+
it.each([
11+
[1, 1, [], 0],
12+
[100, 1, [[10, 100]], -1],
13+
[100, 10, [[10, 60], [20, 30], [30, 30], [60, 40]], 2],
14+
])('示例%#', (target, startFuel, stations, expected) => {
15+
expect(fn(target, startFuel, stations)).toBe(expected)
16+
})
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { PriorityQueue } from '~/utils/priorityQueue'
2+
3+
/**
4+
* 动态规划
5+
* @desc 时间复杂度 O(N²) 空间复杂度 O(N)
6+
* @param target
7+
* @param startFuel
8+
* @param stations
9+
* @returns
10+
*/
11+
export function minRefuelStops(
12+
target: number,
13+
startFuel: number,
14+
stations: number[][],
15+
): number {
16+
const len = stations.length
17+
const dp = new Array(len + 1).fill(0)
18+
dp[0] = startFuel
19+
20+
for (let i = 0; i < len; i++) {
21+
for (let j = i; j >= 0; j--) {
22+
if (dp[j] >= stations[i][0])
23+
dp[j + 1] = Math.max(dp[j + 1], dp[j] + stations[i][1])
24+
}
25+
}
26+
27+
for (let i = 0; i <= len; i++) {
28+
if (dp[i] >= target)
29+
return i
30+
}
31+
32+
return -1
33+
}
34+
35+
/**
36+
* 贪心算法
37+
* @desc 时间复杂度 O(NlogN) 空间复杂度 O(N)
38+
* @param target
39+
* @param startFuel
40+
* @param stations
41+
* @returns
42+
*/
43+
export function minRefuelStops2(
44+
target: number,
45+
startFuel: number,
46+
stations: number[][],
47+
): number {
48+
const pq = new PriorityQueue<number>((a, b) => b - a >= 0)
49+
let ans = 0
50+
let prev = 0
51+
let fuel = startFuel
52+
const len = stations.length
53+
54+
for (let i = 0; i <= len; i++) {
55+
const curr = i < len ? stations[i][0] : target
56+
fuel -= curr - prev
57+
while (fuel < 0 && !pq.isEmpty()) {
58+
fuel += pq.poll()!
59+
ans++
60+
}
61+
62+
if (fuel < 0) return -1
63+
64+
if (i < len) {
65+
pq.offer(stations[i][1])
66+
prev = curr
67+
}
68+
}
69+
return ans
70+
}

utils/priorityQueue.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class PriorityQueue<T> {
77

88
constructor(
99
private _compare: (a: T, b: T) => boolean,
10-
) {}
10+
) { }
1111

1212
/**
1313
* 判断队列是否为空

0 commit comments

Comments
 (0)