Skip to content

Commit 464c38a

Browse files
committed
[1202]ADD:LC-292/877
1 parent d9098c2 commit 464c38a

File tree

3 files changed

+120
-6
lines changed

3 files changed

+120
-6
lines changed

Diff for: readme.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
[[20201027]144. 二叉树的前序遍历-Medium](树/144.%20二叉树的前序遍历-Medium.md)
4040
[[20201029]129. 求根到叶子节点数字之和-Medium](树/129.%20求根到叶子节点数字之和-Medium.md)
4141
## 2020.11
42-
[[20201111]474. 一和零-Medium](动态规划/背包问题/474.%20一和零-Medium.md)
43-
[[20201116]1641. 统计字典序元音字符串的数目-Medium](动态规划/1641.%20统计字典序元音字符串的数目-Medium.md)
44-
[[20201117]剑指 Offer 47. 礼物的最大价值](动态规划/剑指%20Offer%2047.%20礼物的最大价值.md)
42+
[[20201111]474. 一和零-Medium](动态规划/背包问题/474.%20一和零-Medium.md)
43+
[[20201116]1641. 统计字典序元音字符串的数目-Medium](动态规划/1641.%20统计字典序元音字符串的数目-Medium.md)
44+
[[20201117]剑指 Offer 47. 礼物的最大价值](动态规划/剑指%20Offer%2047.%20礼物的最大价值.md)
4545
[[20201118]134. 加油站-Medium](贪心/134.%20加油站-Medium.md)
4646
[[20201119]338. 比特位计数-Medium](动态规划/338.%20比特位计数-Medium.md)
4747
[[20201121]21. 合并两个有序链表-Easy](链表/21.%20合并两个有序链表-Easy.md)
@@ -54,7 +54,8 @@
5454
[[20201130]5. 最长回文子串-Medium](动态规划/5.%20最长回文子串-Medium.md)
5555
## 2020.12
5656
[[20201201]34. 在排序数组中查找元素的第一个和最后一个位置-Medium](数组/34.%20在排序数组中查找元素的第一个和最后一个位置-Medium.md)
57-
57+
[[20201202]292. Nim 游戏-Easy](动态规划/292.%20Nim%20游戏-Easy.md)
58+
[[20201202]877. 石子游戏-Medium](动态规划/877.%20石子游戏-Medium.md)
5859
# 按类别归类
5960
##
6061
[102. 二叉树的层序遍历 - Medium](./树/102.%20二叉树的层序遍历%20-%20Medium.md)
@@ -71,9 +72,9 @@
7172
[剑指 Offer 54. 二叉搜索树的第k大节点 - Easy](./树/剑指%20Offer%2054.%20二叉搜索树的第k大节点%20-%20Easy.md)
7273

7374
## 数组和双指针
74-
[34. 在排序数组中查找元素的第一个和最后一个位置-Medium](数组/34.%20在排序数组中查找元素的第一个和最后一个位置-Medium.md)
7575
[15. 三数之和 - Medium](双指针/15.%20三数之和%20-%20Medium.md)
7676
[18. 四数之和-Medium](双指针/18.%20四数之和-medium.md)
77+
[34. 在排序数组中查找元素的第一个和最后一个位置-Medium](数组/34.%20在排序数组中查找元素的第一个和最后一个位置-Medium.md)
7778
[560. 和为K的子数组-Medium](数组/560.%20和为K的子数组-Medium.md)
7879
[611. 有效三角形的个数 - Medium](./双指针/611.%20有效三角形的个数%20-%20Medium.md)
7980
[845. 数组中的最长山脉-Medium](双指针/845.%20数组中的最长山脉-Medium)
@@ -116,11 +117,12 @@
116117

117118
[5. 最长回文子串-Medium](动态规划/5.%20最长回文子串-Medium.md)
118119
[53. 最大子序和-Easy](动态规划/53.%20最大子序和-Easy.md)
120+
[292. Nim 游戏-Easy](动态规划/292.%20Nim%20游戏-Easy.md)
119121
[338. 比特位计数-Medium](动态规划/338.%20比特位计数-Medium.md)
122+
[877. 石子游戏-Medium](动态规划/877.%20石子游戏-Medium.md)
120123
[1641. 统计字典序元音字符串的数目-Medium](动态规划/1641.%20统计字典序元音字符串的数目-Medium.md)
121124
[剑指 Offer 47. 礼物的最大价值](动态规划/剑指%20Offer%2047.%20礼物的最大价值.md)
122125

123-
124126
## 贪心
125127
[134. 加油站-Medium](贪心/134.%20加油站-Medium.md)
126128
[763. 划分字母区间-Medium](贪心/763.%20划分字母区间-Medium.md)

Diff for: 动态规划/292. Nim 游戏-Easy.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# [Description](https://leetcode-cn.com/problems/nim-game)
2+
你和你的朋友,两个人一起玩 Nim 游戏:
3+
4+
桌子上有一堆石头。
5+
你们轮流进行自己的回合,你作为先手。
6+
每一回合,轮到的人拿掉 1 - 3 块石头。
7+
拿掉最后一块石头的人就是获胜者。
8+
假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。
9+
10+
 
11+
12+
示例 1:
13+
```python
14+
输入:n = 4
15+
输出:false
16+
解释:如果堆中有 4 块石头,那么你永远不会赢得比赛;
17+
  因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。
18+
```
19+
示例 2:
20+
```python
21+
输入:n = 1
22+
输出:true
23+
```
24+
示例 3:
25+
```python
26+
输入:n = 2
27+
输出:true
28+
```
29+
30+
提示:
31+
32+
- $1 <= n <= 2^{31} - 1$
33+
34+
# Solution
35+
- 将石子按1-n排列,假设游戏从index=0开始,则1、2、3均可为获胜点,此时对方可选取的集合为2-4、3-5、4-6,因此对上述三个集合取交集index=4为“我”完全无法选中的石子,以此类推。所以判断n是否可被4整除即可。
36+
- 可用位运算加快计算速度,$X\ mod\ 2^n=X\ \&\ (2^n-1)$
37+
- 时间复杂度:O(1)
38+
- 空间复杂度:O(1)
39+
```python
40+
class Solution:
41+
def canWinNim(self, n: int) -> bool:
42+
return n % 4 != 0
43+
# return (n & 3) != 0
44+
```

Diff for: 动态规划/877. 石子游戏-Medium.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# [Description](https://leetcode-cn.com/problems/stone-game)
2+
亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。
3+
4+
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
5+
6+
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
7+
8+
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。
9+
10+
 
11+
12+
示例:
13+
```python
14+
输入:[5,3,4,5]
15+
输出:true
16+
解释:
17+
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
18+
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
19+
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
20+
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
21+
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。
22+
```
23+
24+
提示:
25+
26+
- 2 <= piles.length <= 500
27+
- piles.length 是偶数。
28+
- 1 <= piles[i] <= 500
29+
- sum(piles) 是奇数。
30+
31+
32+
# Solution
33+
### 1. 动态规划[正经]
34+
- 时间复杂度:$O(n^2)$
35+
- 空间复杂度:$O(n^2)$(可以优化到O(n),但有些复杂)
36+
- 动态规划问题定义
37+
- dp[i][j]表示对piles[i:j+1]这些堆石子,此刻拿石子的人最多能比对方多拿多少个(这个人不见得是alex)
38+
- 那么若当前这个人拿了piles[i],则下一个人要从piles[i+1:j+1]中继续拿;若当前这个人拿了piles[j],则下一个人要从piles[i:j] (i~j-1)中继续拿。
39+
- 因此状态转移方程为:
40+
$$
41+
dp[i][j]=max(piles[i]-dp[i+1][j], piles[j]-dp[i][j-1])
42+
$$
43+
- 最终只需要判断dp[0][len(piles)-1]是否大于0即可(alex是否能比对方多)
44+
```python
45+
class Solution:
46+
def stoneGame(self, piles: List[int]) -> bool:
47+
# return True
48+
dp = [0]*len(piles)
49+
for i in range(len(piles)):
50+
dp[i] = piles[i]
51+
52+
for i in range(len(piles)-1):
53+
for j in range(i+1, len(piles)):
54+
dp[i][j] = max(piles[i]-dp[i+1][j], piles[j]-dp[i][j-1])
55+
56+
return dp[0][len(piles)-1] > 0
57+
```
58+
59+
### 2. 数学方法(偷袭!)
60+
- 把偶数堆石子编号1-n,由此可按编号分为奇数堆和偶数堆
61+
- 在游戏最开始,若alex拿了奇数堆,则lee两头都是偶数堆;且lee拿完之后alex又可以选奇数堆or偶数堆拿取。
62+
- 所以alex只要算出奇数堆和偶数堆谁更石子数量更多,即可赢得游戏胜利
63+
```python
64+
class Solution:
65+
def stoneGame(self, piles: List[int]) -> bool:
66+
# 偷袭!
67+
return True
68+
```

0 commit comments

Comments
 (0)