Skip to content

Commit e4be115

Browse files
committed
feat: leetcode 741
1 parent 6d96c07 commit e4be115

File tree

8 files changed

+168
-1
lines changed

8 files changed

+168
-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/-进度:426-green" alt="进度:426">
5+
<img src="https://img.shields.io/badge/-进度:427-green" alt="进度:427">
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
@@ -1429,6 +1429,12 @@
14291429
"path": "./problemset/count-different-palindromic-subsequences/README.md",
14301430
"difficulty": "困难"
14311431
},
1432+
{
1433+
"id": "741",
1434+
"title": "摘樱桃",
1435+
"path": "./problemset/cherry-pickup/README.md",
1436+
"difficulty": "困难"
1437+
},
14321438
{
14331439
"id": "871",
14341440
"title": "最低加油次数",

assets/data/topics.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3399,6 +3399,16 @@
33993399
"url": "https://leetcode.cn/problems/parse-lisp-expression/",
34003400
"path": "./problemset/parse-lisp-expression/README.md"
34013401
},
3402+
{
3403+
"id": "741",
3404+
"title": {
3405+
"cn": "摘樱桃",
3406+
"en": "cherry-pickup"
3407+
},
3408+
"difficulty": "困难",
3409+
"url": "https://leetcode.cn/problems/cherry-pickup/",
3410+
"path": "./problemset/cherry-pickup/README.md"
3411+
},
34023412
{
34033413
"id": "744",
34043414
"title": {

assets/docs/CATEGORIES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@
271271
| 473. [火柴拼正方形](../../problemset/matchsticks-to-square/README.md) | 中等 |
272272
| 688. [骑士在棋盘上的概率](../../problemset/knight-probability-in-chessboard/README.md) | 中等 |
273273
| 730. [统计不同回文子序列](../../problemset/count-different-palindromic-subsequences/README.md) | 困难 |
274+
| 741. [摘樱桃](../../problemset/cherry-pickup/README.md) | 困难 |
274275
| 871. [最低加油次数](../../problemset/minimum-number-of-refueling-stops/README.md) | 困难 |
275276
| 873. [最长的斐波那契子序列的长度](../../problemset/length-of-longest-fibonacci-subsequence/README.md) | 中等 |
276277
| 926. [将字符串翻转到单调递增](../../problemset/flip-string-to-monotone-increasing/README.md) | 中等 |

assets/docs/TOPICS.md

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

681681
[736. Lisp 语法解析](../../problemset/parse-lisp-expression/README.md)
682682

683+
[741. 摘樱桃](../../problemset/cherry-pickup/README.md)
684+
683685
[744. 寻找比目标字母大的最小字母](../../problemset/find-smallest-letter-greater-than-target/README.md)
684686

685687
[762. 二进制表示中质数个计算置位](../../problemset/prime-number-of-set-bits-in-binary-representation/README.md)

problemset/cherry-pickup/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# 摘樱桃
2+
3+
> 难度:困难
4+
>
5+
> https://leetcode.cn/problems/cherry-pickup/
6+
7+
## 题目
8+
9+
一个`N x N`的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:
10+
11+
- `0` 表示这个格子是空的,所以你可以穿过它。
12+
- `1` 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。
13+
- `-1` 表示这个格子里有荆棘,挡着你的路。
14+
15+
你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:
16+
17+
- 从位置 `(0, 0)` 出发,最后到达` (N-1, N-1)` ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为`0`或者`1`的格子);
18+
- 当到达 `(N-1, N-1)` 后,你要继续走,直到返回到` (0, 0)` ,只能向上或向左走,并且只能穿越有效的格子;
19+
- 当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为`0`);
20+
- 如果在 `(0, 0)``(N-1, N-1)` 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。
21+
22+
23+
### 示例
24+
25+
```
26+
输入: grid =
27+
[[0, 1, -1],
28+
[1, 0, -1],
29+
[1, 1, 1]]
30+
输出: 5
31+
解释:
32+
玩家从(0,0)点出发,经过了向下走,向下走,向右走,向右走,到达了点(2, 2)。
33+
在这趟单程中,总共摘到了4颗樱桃,矩阵变成了[[0,1,-1],[0,0,-1],[0,0,0]]。
34+
接着,这名玩家向左走,向上走,向上走,向左走,返回了起始点,又摘到了1颗樱桃。
35+
在旅程中,总共摘到了5颗樱桃,这是可以摘到的最大值了。
36+
```
37+
38+
## 解题
39+
40+
```ts
41+
/**
42+
* 动态规划
43+
* @desc 时间复杂度 O(N³) 空间复杂度 O(N²)
44+
* @param grid
45+
* @returns
46+
*/
47+
export function cherryPickup(grid: number[][]): number {
48+
const len = grid.length
49+
const dp: number[][] = new Array(len).fill([])
50+
.map(() => new Array(len).fill(-Number.MAX_VALUE))
51+
dp[0][0] = grid[0][0]
52+
53+
for (let i = 1; i < len * 2 - 1; i++) {
54+
for (let x1 = Math.min(i, len - 1); x1 >= Math.max(i - len + 1, 0); x1--) {
55+
for (let x2 = Math.min(i, len - 1); x2 >= x1; x2--) {
56+
const y1 = i - x1
57+
const y2 = i - x2
58+
if (grid[x1][y1] === -1 || grid[x2][y2] === -1) {
59+
dp[x1][x2] = -Number.MAX_VALUE
60+
continue
61+
}
62+
63+
let res = dp[x1][x2] // 都往右
64+
65+
if (x1 > 0)
66+
res = Math.max(res, dp[x1 - 1][x2]) // 往下,往右
67+
if (x2 > 0)
68+
res = Math.max(res, dp[x1][x2 - 1]) // 往右,往下
69+
if (x1 > 0 && x2 > 0)
70+
res = Math.max(res, dp[x1 - 1][x2 - 1]) // 都往下
71+
72+
res += grid[x1][y1]
73+
// 避免重复摘同一个樱桃
74+
if (x2 !== x1)
75+
res += grid[x2][y2]
76+
77+
dp[x1][x2] = res
78+
}
79+
}
80+
}
81+
82+
return Math.max(dp[len - 1][len - 1], 0)
83+
}
84+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { cherryPickup } from '.'
3+
4+
describe('摘樱桃', () => {
5+
testCase(cherryPickup)
6+
})
7+
8+
function testCase(fn: (grid: number[][]) => number) {
9+
it.each([
10+
[
11+
[
12+
[0, 1, -1],
13+
[1, 0, -1],
14+
[1, 1, 1],
15+
],
16+
5,
17+
],
18+
])('示例%#', (grid, expected) => {
19+
expect(fn(grid)).toBe(expected)
20+
})
21+
}

problemset/cherry-pickup/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* 动态规划
3+
* @desc 时间复杂度 O(N³) 空间复杂度 O(N²)
4+
* @param grid
5+
* @returns
6+
*/
7+
export function cherryPickup(grid: number[][]): number {
8+
const len = grid.length
9+
const dp: number[][] = new Array(len).fill([])
10+
.map(() => new Array(len).fill(-Number.MAX_VALUE))
11+
dp[0][0] = grid[0][0]
12+
13+
for (let i = 1; i < len * 2 - 1; i++) {
14+
for (let x1 = Math.min(i, len - 1); x1 >= Math.max(i - len + 1, 0); x1--) {
15+
for (let x2 = Math.min(i, len - 1); x2 >= x1; x2--) {
16+
const y1 = i - x1
17+
const y2 = i - x2
18+
if (grid[x1][y1] === -1 || grid[x2][y2] === -1) {
19+
dp[x1][x2] = -Number.MAX_VALUE
20+
continue
21+
}
22+
23+
let res = dp[x1][x2] // 都往右
24+
25+
if (x1 > 0)
26+
res = Math.max(res, dp[x1 - 1][x2]) // 往下,往右
27+
if (x2 > 0)
28+
res = Math.max(res, dp[x1][x2 - 1]) // 往右,往下
29+
if (x1 > 0 && x2 > 0)
30+
res = Math.max(res, dp[x1 - 1][x2 - 1]) // 都往下
31+
32+
res += grid[x1][y1]
33+
// 避免重复摘同一个樱桃
34+
if (x2 !== x1)
35+
res += grid[x2][y2]
36+
37+
dp[x1][x2] = res
38+
}
39+
}
40+
}
41+
42+
return Math.max(dp[len - 1][len - 1], 0)
43+
}

0 commit comments

Comments
 (0)