Skip to content

Commit 52ea485

Browse files
authored
feat: add solutions to lc problem: No.2029 (doocs#3250)
No.2029.Stone Game IX
1 parent 34c9607 commit 52ea485

File tree

7 files changed

+260
-163
lines changed

7 files changed

+260
-163
lines changed

solution/2000-2099/2029.Stone Game IX/README.md

Lines changed: 93 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,19 @@ Alice 输掉游戏,因为已移除石子值总和(15)可以被 3 整除,
8484

8585
<!-- solution:start -->
8686

87-
### 方法一
87+
### 方法一:贪心 + 分情况讨论
88+
89+
由于玩家的目标是使得已移除石子的价值总和不能被 $3$ 整除,因此我们只需要考虑每个石子的价值对 $3$ 的余数即可。
90+
91+
我们用一个长度为 $3$ 的数组 $\textit{cnt}$ 维护当前剩余石子的价值对 $3$ 的余数的个数,其中 $\textit{cnt}[0]$ 表示余数为 $0$ 的个数,而 $\textit{cnt}[1]$ 和 $\textit{cnt}[2]$ 分别表示余数为 $1$ 和 $2$ 的个数。
92+
93+
在第一回合,Alice 不能移除余数为 $0$ 的石子,因为这样会使得已移除石子的价值总和能被 $3$ 整除。因此,Alice 只能移除余数为 $1$ 或 $2$ 的石子。
94+
95+
我们首先考虑 Alice 移除余数为 $1$ 的石子的情况。如果 Alice 移除了一个余数为 $1$ 的石子,石子 $0$ 对石子价值总和对 $3$ 的余数不会改变,因此价值对 $3$ 的余数为 $0$ 的石子可以在任意回合被移除,我们暂时不考虑。所以 Bob 也只能移除余数为 $1$ 的石子,之后 Alice 移除余数为 $2$ 的石子,依次进行,序列为 $1, 1, 2, 1, 2, \ldots$。在这种情况下,如果最终回合数为奇数,且还有剩余石子,那么 Alice 获胜,否则 Bob 获胜。
96+
97+
对于第一回合 Alice 移除余数为 $2$ 的石子的情况,我们可以得到类似的结论。
98+
99+
时间复杂度 $O(n)$,其中 $n$ 是数组 $\textit{stones}$ 的长度。空间复杂度 $O(1)$。
88100

89101
<!-- tabs:start -->
90102

@@ -93,47 +105,46 @@ Alice 输掉游戏,因为已移除石子值总和(15)可以被 3 整除,
93105
```python
94106
class Solution:
95107
def stoneGameIX(self, stones: List[int]) -> bool:
96-
def check(c):
97-
if c[1] == 0:
108+
def check(cnt: List[int]) -> bool:
109+
if cnt[1] == 0:
98110
return False
99-
c[1] -= 1
100-
turn = 1 + min(c[1], c[2]) * 2 + c[0]
101-
if c[1] > c[2]:
102-
turn += 1
103-
c[1] -= 1
104-
return turn % 2 == 1 and c[1] != c[2]
105-
106-
c = [0] * 3
107-
for s in stones:
108-
c[s % 3] += 1
109-
c1 = [c[0], c[2], c[1]]
110-
return check(c) or check(c1)
111+
cnt[1] -= 1
112+
r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0]
113+
if cnt[1] > cnt[2]:
114+
cnt[1] -= 1
115+
r += 1
116+
return r % 2 == 1 and cnt[1] != cnt[2]
117+
118+
c1 = [0] * 3
119+
for x in stones:
120+
c1[x % 3] += 1
121+
c2 = [c1[0], c1[2], c1[1]]
122+
return check(c1) or check(c2)
111123
```
112124

113125
#### Java
114126

115127
```java
116128
class Solution {
117129
public boolean stoneGameIX(int[] stones) {
118-
int[] c = new int[3];
119-
for (int s : stones) {
120-
++c[s % 3];
130+
int[] c1 = new int[3];
131+
for (int x : stones) {
132+
c1[x % 3]++;
121133
}
122-
int[] t = new int[] {c[0], c[2], c[1]};
123-
return check(c) || check(t);
134+
int[] c2 = {c1[0], c1[2], c1[1]};
135+
return check(c1) || check(c2);
124136
}
125137

126-
private boolean check(int[] c) {
127-
if (c[1] == 0) {
138+
private boolean check(int[] cnt) {
139+
if (--cnt[1] < 0) {
128140
return false;
129141
}
130-
--c[1];
131-
int turn = 1 + Math.min(c[1], c[2]) * 2 + c[0];
132-
if (c[1] > c[2]) {
133-
--c[1];
134-
++turn;
142+
int r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0];
143+
if (cnt[1] > cnt[2]) {
144+
--cnt[1];
145+
++r;
135146
}
136-
return turn % 2 == 1 && c[1] != c[2];
147+
return r % 2 == 1 && cnt[1] != cnt[2];
137148
}
138149
}
139150
```
@@ -144,21 +155,23 @@ class Solution {
144155
class Solution {
145156
public:
146157
bool stoneGameIX(vector<int>& stones) {
147-
vector<int> c(3);
148-
for (int s : stones) ++c[s % 3];
149-
vector<int> t = {c[0], c[2], c[1]};
150-
return check(c) || check(t);
151-
}
152-
153-
bool check(vector<int>& c) {
154-
if (c[1] == 0) return false;
155-
--c[1];
156-
int turn = 1 + min(c[1], c[2]) * 2 + c[0];
157-
if (c[1] > c[2]) {
158-
--c[1];
159-
++turn;
158+
vector<int> c1(3);
159+
for (int x : stones) {
160+
++c1[x % 3];
160161
}
161-
return turn % 2 == 1 && c[1] != c[2];
162+
vector<int> c2 = {c1[0], c1[2], c1[1]};
163+
auto check = [](auto& cnt) -> bool {
164+
if (--cnt[1] < 0) {
165+
return false;
166+
}
167+
int r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0];
168+
if (cnt[1] > cnt[2]) {
169+
--cnt[1];
170+
++r;
171+
}
172+
return r % 2 && cnt[1] != cnt[2];
173+
};
174+
return check(c1) || check(c2);
162175
}
163176
};
164177
```
@@ -167,23 +180,48 @@ public:
167180
168181
```go
169182
func stoneGameIX(stones []int) bool {
170-
check := func(c [3]int) bool {
171-
if c[1] == 0 {
183+
c1 := [3]int{}
184+
for _, x := range stones {
185+
c1[x%3]++
186+
}
187+
c2 := [3]int{c1[0], c1[2], c1[1]}
188+
check := func(cnt [3]int) bool {
189+
if cnt[1] == 0 {
172190
return false
173191
}
174-
c[1]--
175-
turn := 1 + min(c[1], c[2])*2 + c[0]
176-
if c[1] > c[2] {
177-
c[1]--
178-
turn++
192+
cnt[1]--
193+
r := 1 + min(cnt[1], cnt[2])*2 + cnt[0]
194+
if cnt[1] > cnt[2] {
195+
cnt[1]--
196+
r++
179197
}
180-
return turn%2 == 1 && c[1] != c[2]
198+
return r%2 == 1 && cnt[1] != cnt[2]
181199
}
182-
c := [3]int{}
183-
for _, s := range stones {
184-
c[s%3]++
185-
}
186-
return check(c) || check([3]int{c[0], c[2], c[1]})
200+
return check(c1) || check(c2)
201+
}
202+
```
203+
204+
#### TypeScript
205+
206+
```ts
207+
function stoneGameIX(stones: number[]): boolean {
208+
const c1: number[] = Array(3).fill(0);
209+
for (const x of stones) {
210+
++c1[x % 3];
211+
}
212+
const c2: number[] = [c1[0], c1[2], c1[1]];
213+
const check = (cnt: number[]): boolean => {
214+
if (--cnt[1] < 0) {
215+
return false;
216+
}
217+
let r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0];
218+
if (cnt[1] > cnt[2]) {
219+
--cnt[1];
220+
++r;
221+
}
222+
return r % 2 === 1 && cnt[1] !== cnt[2];
223+
};
224+
return check(c1) || check(c2);
187225
}
188226
```
189227

solution/2000-2099/2029.Stone Game IX/README_EN.md

Lines changed: 93 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,19 @@ Alice loses the game because the sum of the removed stones (15) is divisible by
7777

7878
<!-- solution:start -->
7979

80-
### Solution 1
80+
### Solution 1: Greedy + Case Discussion
81+
82+
Since the player's goal is to ensure the total value of the removed stones is not divisible by $3$, we only need to consider the remainder of each stone's value when divided by $3$.
83+
84+
We use an array $\textit{cnt}$ of length $3$ to maintain the count of the current remaining stones' values modulo $3$, where $\textit{cnt}[0]$ represents the count of stones with a remainder of $0$, and $\textit{cnt}[1]$ and $\textit{cnt}[2]$ respectively represent the counts of stones with remainders of $1$ and $2$.
85+
86+
In the first round, Alice cannot remove stones with a remainder of $0$, as this would make the total value of the removed stones divisible by $3$. Therefore, Alice can only remove stones with a remainder of $1$ or $2$.
87+
88+
First, let's consider the case where Alice removes a stone with a remainder of $1$. If Alice removes a stone with a remainder of $1$, the remainder of the total value of stones $0$ against $3$ will not change, so stones with a value remainder of $0$ can be removed in any round, which we will not consider for now. Thus, Bob can only remove stones with a remainder of $1$, followed by Alice removing stones with a remainder of $2$, and so on, in the sequence $1, 1, 2, 1, 2, \ldots$. In this scenario, if the final round is odd and there are still remaining stones, then Alice wins; otherwise, Bob wins.
89+
90+
For the case where Alice removes a stone with a remainder of $2$ in the first round, we can draw a similar conclusion.
91+
92+
The time complexity is $O(n)$, where $n$ is the length of the array $\textit{stones}$. The space complexity is $O(1)$.
8193

8294
<!-- tabs:start -->
8395

@@ -86,47 +98,46 @@ Alice loses the game because the sum of the removed stones (15) is divisible by
8698
```python
8799
class Solution:
88100
def stoneGameIX(self, stones: List[int]) -> bool:
89-
def check(c):
90-
if c[1] == 0:
101+
def check(cnt: List[int]) -> bool:
102+
if cnt[1] == 0:
91103
return False
92-
c[1] -= 1
93-
turn = 1 + min(c[1], c[2]) * 2 + c[0]
94-
if c[1] > c[2]:
95-
turn += 1
96-
c[1] -= 1
97-
return turn % 2 == 1 and c[1] != c[2]
98-
99-
c = [0] * 3
100-
for s in stones:
101-
c[s % 3] += 1
102-
c1 = [c[0], c[2], c[1]]
103-
return check(c) or check(c1)
104+
cnt[1] -= 1
105+
r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0]
106+
if cnt[1] > cnt[2]:
107+
cnt[1] -= 1
108+
r += 1
109+
return r % 2 == 1 and cnt[1] != cnt[2]
110+
111+
c1 = [0] * 3
112+
for x in stones:
113+
c1[x % 3] += 1
114+
c2 = [c1[0], c1[2], c1[1]]
115+
return check(c1) or check(c2)
104116
```
105117

106118
#### Java
107119

108120
```java
109121
class Solution {
110122
public boolean stoneGameIX(int[] stones) {
111-
int[] c = new int[3];
112-
for (int s : stones) {
113-
++c[s % 3];
123+
int[] c1 = new int[3];
124+
for (int x : stones) {
125+
c1[x % 3]++;
114126
}
115-
int[] t = new int[] {c[0], c[2], c[1]};
116-
return check(c) || check(t);
127+
int[] c2 = {c1[0], c1[2], c1[1]};
128+
return check(c1) || check(c2);
117129
}
118130

119-
private boolean check(int[] c) {
120-
if (c[1] == 0) {
131+
private boolean check(int[] cnt) {
132+
if (--cnt[1] < 0) {
121133
return false;
122134
}
123-
--c[1];
124-
int turn = 1 + Math.min(c[1], c[2]) * 2 + c[0];
125-
if (c[1] > c[2]) {
126-
--c[1];
127-
++turn;
135+
int r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0];
136+
if (cnt[1] > cnt[2]) {
137+
--cnt[1];
138+
++r;
128139
}
129-
return turn % 2 == 1 && c[1] != c[2];
140+
return r % 2 == 1 && cnt[1] != cnt[2];
130141
}
131142
}
132143
```
@@ -137,21 +148,23 @@ class Solution {
137148
class Solution {
138149
public:
139150
bool stoneGameIX(vector<int>& stones) {
140-
vector<int> c(3);
141-
for (int s : stones) ++c[s % 3];
142-
vector<int> t = {c[0], c[2], c[1]};
143-
return check(c) || check(t);
144-
}
145-
146-
bool check(vector<int>& c) {
147-
if (c[1] == 0) return false;
148-
--c[1];
149-
int turn = 1 + min(c[1], c[2]) * 2 + c[0];
150-
if (c[1] > c[2]) {
151-
--c[1];
152-
++turn;
151+
vector<int> c1(3);
152+
for (int x : stones) {
153+
++c1[x % 3];
153154
}
154-
return turn % 2 == 1 && c[1] != c[2];
155+
vector<int> c2 = {c1[0], c1[2], c1[1]};
156+
auto check = [](auto& cnt) -> bool {
157+
if (--cnt[1] < 0) {
158+
return false;
159+
}
160+
int r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0];
161+
if (cnt[1] > cnt[2]) {
162+
--cnt[1];
163+
++r;
164+
}
165+
return r % 2 && cnt[1] != cnt[2];
166+
};
167+
return check(c1) || check(c2);
155168
}
156169
};
157170
```
@@ -160,23 +173,48 @@ public:
160173
161174
```go
162175
func stoneGameIX(stones []int) bool {
163-
check := func(c [3]int) bool {
164-
if c[1] == 0 {
176+
c1 := [3]int{}
177+
for _, x := range stones {
178+
c1[x%3]++
179+
}
180+
c2 := [3]int{c1[0], c1[2], c1[1]}
181+
check := func(cnt [3]int) bool {
182+
if cnt[1] == 0 {
165183
return false
166184
}
167-
c[1]--
168-
turn := 1 + min(c[1], c[2])*2 + c[0]
169-
if c[1] > c[2] {
170-
c[1]--
171-
turn++
185+
cnt[1]--
186+
r := 1 + min(cnt[1], cnt[2])*2 + cnt[0]
187+
if cnt[1] > cnt[2] {
188+
cnt[1]--
189+
r++
172190
}
173-
return turn%2 == 1 && c[1] != c[2]
191+
return r%2 == 1 && cnt[1] != cnt[2]
174192
}
175-
c := [3]int{}
176-
for _, s := range stones {
177-
c[s%3]++
178-
}
179-
return check(c) || check([3]int{c[0], c[2], c[1]})
193+
return check(c1) || check(c2)
194+
}
195+
```
196+
197+
#### TypeScript
198+
199+
```ts
200+
function stoneGameIX(stones: number[]): boolean {
201+
const c1: number[] = Array(3).fill(0);
202+
for (const x of stones) {
203+
++c1[x % 3];
204+
}
205+
const c2: number[] = [c1[0], c1[2], c1[1]];
206+
const check = (cnt: number[]): boolean => {
207+
if (--cnt[1] < 0) {
208+
return false;
209+
}
210+
let r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0];
211+
if (cnt[1] > cnt[2]) {
212+
--cnt[1];
213+
++r;
214+
}
215+
return r % 2 === 1 && cnt[1] !== cnt[2];
216+
};
217+
return check(c1) || check(c2);
180218
}
181219
```
182220

0 commit comments

Comments
 (0)