Skip to content

Commit 6c206a2

Browse files
committed
feat: leetcode 1687
1 parent 0739537 commit 6c206a2

File tree

8 files changed

+207
-1
lines changed

8 files changed

+207
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
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/-进度:562-green" alt="进度:562">
5+
<img src="https://img.shields.io/badge/-进度:563-green" alt="进度:563">
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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,12 @@
17771777
"path": "./problemset/maximum-repeating-substring/README.md",
17781778
"difficulty": "简单"
17791779
},
1780+
{
1781+
"id": "1687",
1782+
"title": "从仓库到码头运输箱子",
1783+
"path": "./problemset/delivering-boxes-from-storage-to-ports/README.md",
1784+
"difficulty": "困难"
1785+
},
17801786
{
17811787
"id": "1774",
17821788
"title": "最接近目标价格的甜点成本",

assets/data/topics.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5069,6 +5069,16 @@
50695069
"url": "https://leetcode.cn/problems/count-the-number-of-consistent-strings/",
50705070
"path": "./problemset/count-the-number-of-consistent-strings/README.md"
50715071
},
5072+
{
5073+
"id": "1687",
5074+
"title": {
5075+
"cn": "从仓库到码头运输箱子",
5076+
"en": "delivering-boxes-from-storage-to-ports"
5077+
},
5078+
"difficulty": "困难",
5079+
"url": "https://leetcode.cn/problems/delivering-boxes-from-storage-to-ports/",
5080+
"path": "./problemset/delivering-boxes-from-storage-to-ports/README.md"
5081+
},
50725082
{
50735083
"id": "1694",
50745084
"title": {

assets/docs/CATEGORIES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
| 940. [不同的子序列 II](../../problemset/distinct-subsequences-2/README.md) | 困难 |
330330
| 1235. [规划兼职工作](../../problemset/maximum-profit-in-job-scheduling/README.md) | 困难 |
331331
| 1668. [最大重复子字符串](../../problemset/maximum-repeating-substring/README.md) | 简单 |
332+
| 1687. [从仓库到码头运输箱子](../../problemset/delivering-boxes-from-storage-to-ports/README.md) | 困难 |
332333
| 1774. [最接近目标价格的甜点成本](../../problemset/closest-dessert-cost/README.md) | 中等 |
333334
| 1800. [最大升序子数组和](../../problemset/maximum-ascending-subarray-sum/README.md) | 简单 |
334335
| 1994. [好子集的数目](../../problemset/the-number-of-good-subsets/README.md) | 困难 |

assets/docs/TOPICS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,8 @@
10141014

10151015
[1684. 统计一致字符串的数目](../../problemset/count-the-number-of-consistent-strings/README.md)
10161016

1017+
[1687. 从仓库到码头运输箱子](../../problemset/delivering-boxes-from-storage-to-ports/README.md)
1018+
10171019
[1694. 重新格式化电话号码](../../problemset/reformat-phone-number/README.md)
10181020

10191021
[1700. 无法吃午餐的学生数量](../../problemset/number-of-students-unable-to-eat-lunch/README.md)
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# 从仓库到码头运输箱子
2+
3+
> 难度:困难
4+
>
5+
> https://leetcode.cn/problems/delivering-boxes-from-storage-to-ports/
6+
7+
## 题目
8+
9+
你有一辆货运卡车,你需要用这一辆车把一些箱子从仓库运送到码头。这辆卡车每次运输有 **箱子数目的限制****总重量的限制**
10+
11+
给你一个箱子数组 `boxes` 和三个整数 `portsCount`, `maxBoxes``maxWeight` ,其中 `boxes[i] = [ports​​i​, weighti]`
12+
13+
- `ports​​i` 表示第 `i` 个箱子需要送达的码头, `weightsi` 是第 `i` 个箱子的重量。
14+
- `portsCount` 是码头的数目。
15+
- `maxBoxes``maxWeight` 分别是卡车每趟运输箱子数目和重量的限制。
16+
17+
箱子需要按照 **数组顺序** 运输,同时每次运输需要遵循以下步骤:
18+
19+
- 卡车从 `boxes` 队列中按顺序取出若干个箱子,但不能违反 `maxBoxes``maxWeight` 限制。
20+
- 对于在卡车上的箱子,我们需要 **按顺序** 处理它们,卡车会通过 **一趟行程** 将最前面的箱子送到目的地码头并卸货。如果卡车已经在对应的码头,那么不需要 **额外行程** ,箱子也会立马被卸货。
21+
- 卡车上所有箱子都被卸货后,卡车需要 **一趟行程** 回到仓库,从箱子队列里再取出一些箱子。
22+
23+
卡车在将所有箱子运输并卸货后,最后必须回到仓库。
24+
25+
请你返回将所有箱子送到相应码头的 **最少行程** 次数。
26+
27+
### 示例
28+
29+
#### 示例 1:
30+
31+
```
32+
输入:boxes = [[1,1],[2,1],[1,1]], portsCount = 2, maxBoxes = 3, maxWeight = 3
33+
输出:4
34+
解释:最优策略如下:
35+
- 卡车将所有箱子装上车,到达码头 1 ,然后去码头 2 ,然后再回到码头 1 ,最后回到仓库,总共需要 4 趟行程。
36+
所以总行程数为 4 。
37+
注意到第一个和第三个箱子不能同时被卸货,因为箱子需要按顺序处理(也就是第二个箱子需要先被送到码头 2 ,然后才能处理第三个箱子)。
38+
```
39+
40+
#### 示例 2:
41+
42+
```
43+
输入:boxes = [[1,2],[3,3],[3,1],[3,1],[2,4]], portsCount = 3, maxBoxes = 3, maxWeight = 6
44+
输出:6
45+
解释:最优策略如下:
46+
- 卡车首先运输第一个箱子,到达码头 1 ,然后回到仓库,总共 2 趟行程。
47+
- 卡车运输第二、第三、第四个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
48+
- 卡车运输第五个箱子,到达码头 3 ,回到仓库,总共 2 趟行程。
49+
总行程数为 2 + 2 + 2 = 6 。
50+
```
51+
52+
#### 示例 3:
53+
```
54+
输入:boxes = [[1,4],[1,2],[2,1],[2,1],[3,2],[3,4]], portsCount = 3, maxBoxes = 6, maxWeight = 7
55+
输出:6
56+
解释:最优策略如下:
57+
- 卡车运输第一和第二个箱子,到达码头 1 ,然后回到仓库,总共 2 趟行程。
58+
- 卡车运输第三和第四个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
59+
- 卡车运输第五和第六个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
60+
总行程数为 2 + 2 + 2 = 6 。
61+
```
62+
63+
#### 示例 4:
64+
65+
```
66+
输入:boxes = [[2,4],[2,5],[3,1],[3,2],[3,7],[3,1],[4,4],[1,3],[5,2]], portsCount = 5, maxBoxes = 5, maxWeight = 7
67+
输出:14
68+
解释:最优策略如下:
69+
- 卡车运输第一个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
70+
- 卡车运输第二个箱子,到达码头 2 ,然后回到仓库,总共 2 趟行程。
71+
- 卡车运输第三和第四个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
72+
- 卡车运输第五个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
73+
- 卡车运输第六和第七个箱子,到达码头 3 ,然后去码头 4 ,然后回到仓库,总共 3 趟行程。
74+
- 卡车运输第八和第九个箱子,到达码头 1 ,然后去码头 5 ,然后回到仓库,总共 3 趟行程。
75+
总行程数为 2 + 2 + 2 + 2 + 3 + 3 = 14 。
76+
```
77+
78+
## 解题
79+
80+
```ts
81+
/**
82+
* 动态规划 + 单调队列优化
83+
* @desc 时间复杂度 O(n) 空间复杂度 O(n)
84+
* @param boxes
85+
* @param portsCount
86+
* @param maxBoxes
87+
* @param maxWeight
88+
* @returns
89+
*/
90+
export function boxDelivering(boxes: number[][], portsCount: number, maxBoxes: number, maxWeight: number): number {
91+
const n = boxes.length
92+
const p = new Array(n + 1).fill(0)
93+
const w = new Array(n + 1).fill(0)
94+
const neg = new Array(n + 1).fill(0)
95+
const W = new Array(n + 1).fill(0)
96+
for (let i = 1; i <= n; ++i) {
97+
p[i] = boxes[i - 1][0]
98+
w[i] = boxes[i - 1][1]
99+
if (i > 1)
100+
neg[i] = neg[i - 1] + (p[i - 1] !== p[i] ? 1 : 0)
101+
102+
W[i] = W[i - 1] + w[i]
103+
}
104+
105+
const opt = [0]
106+
const f = new Array(n + 1).fill(0)
107+
const g = new Array(n + 1).fill(0)
108+
for (let i = 1; i <= n; ++i) {
109+
while (i - opt[0] > maxBoxes || W[i] - W[opt[0]] > maxWeight)
110+
opt.shift()
111+
112+
f[i] = g[opt[0]] + neg[i] + 2
113+
114+
if (i !== n) {
115+
g[i] = f[i] - neg[i + 1]
116+
while (opt.length && g[i] <= g[opt[opt.length - 1]])
117+
opt.pop()
118+
119+
opt.push(i)
120+
}
121+
}
122+
123+
return f[n]
124+
}
125+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { boxDelivering } from '.'
3+
4+
describe('从仓库到码头运输箱子', () => {
5+
testCase(boxDelivering)
6+
})
7+
8+
function testCase(fn: (boxes: number[][], portsCount: number, maxBoxes: number, maxWeight: number) => number) {
9+
it.each([
10+
[[[1, 1], [2, 1], [1, 1]], 2, 3, 3, 4],
11+
[[[1, 2], [3, 3], [3, 1], [3, 1], [2, 4]], 3, 3, 6, 6],
12+
[[[1, 4], [1, 2], [2, 1], [2, 1], [3, 2], [3, 4]], 3, 6, 7, 6],
13+
[[[2, 4], [2, 5], [3, 1], [3, 2], [3, 7], [3, 1], [4, 4], [1, 3], [5, 2]], 5, 5, 7, 14],
14+
15+
])('示例%#', (boxes, portsCount, maxBoxes, maxWeight, expected) => {
16+
expect(fn(boxes, portsCount, maxBoxes, maxWeight)).toBe(expected)
17+
})
18+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* 动态规划 + 单调队列优化
3+
* @desc 时间复杂度 O(n) 空间复杂度 O(n)
4+
* @param boxes
5+
* @param portsCount
6+
* @param maxBoxes
7+
* @param maxWeight
8+
* @returns
9+
*/
10+
export function boxDelivering(boxes: number[][], portsCount: number, maxBoxes: number, maxWeight: number): number {
11+
const n = boxes.length
12+
const p = new Array(n + 1).fill(0)
13+
const w = new Array(n + 1).fill(0)
14+
const neg = new Array(n + 1).fill(0)
15+
const W = new Array(n + 1).fill(0)
16+
for (let i = 1; i <= n; ++i) {
17+
p[i] = boxes[i - 1][0]
18+
w[i] = boxes[i - 1][1]
19+
if (i > 1)
20+
neg[i] = neg[i - 1] + (p[i - 1] !== p[i] ? 1 : 0)
21+
22+
W[i] = W[i - 1] + w[i]
23+
}
24+
25+
const opt = [0]
26+
const f = new Array(n + 1).fill(0)
27+
const g = new Array(n + 1).fill(0)
28+
for (let i = 1; i <= n; ++i) {
29+
while (i - opt[0] > maxBoxes || W[i] - W[opt[0]] > maxWeight)
30+
opt.shift()
31+
32+
f[i] = g[opt[0]] + neg[i] + 2
33+
34+
if (i !== n) {
35+
g[i] = f[i] - neg[i + 1]
36+
while (opt.length && g[i] <= g[opt[opt.length - 1]])
37+
opt.pop()
38+
39+
opt.push(i)
40+
}
41+
}
42+
43+
return f[n]
44+
}

0 commit comments

Comments
 (0)