diff --git a/solution/2000-2099/2029.Stone Game IX/README.md b/solution/2000-2099/2029.Stone Game IX/README.md index 3d77ab7e0a4c1..a58260bfbd691 100644 --- a/solution/2000-2099/2029.Stone Game IX/README.md +++ b/solution/2000-2099/2029.Stone Game IX/README.md @@ -84,7 +84,19 @@ Alice 输掉游戏,因为已移除石子值总和(15)可以被 3 整除, -### 方法一 +### 方法一:贪心 + 分情况讨论 + +由于玩家的目标是使得已移除石子的价值总和不能被 $3$ 整除,因此我们只需要考虑每个石子的价值对 $3$ 的余数即可。 + +我们用一个长度为 $3$ 的数组 $\textit{cnt}$ 维护当前剩余石子的价值对 $3$ 的余数的个数,其中 $\textit{cnt}[0]$ 表示余数为 $0$ 的个数,而 $\textit{cnt}[1]$ 和 $\textit{cnt}[2]$ 分别表示余数为 $1$ 和 $2$ 的个数。 + +在第一回合,Alice 不能移除余数为 $0$ 的石子,因为这样会使得已移除石子的价值总和能被 $3$ 整除。因此,Alice 只能移除余数为 $1$ 或 $2$ 的石子。 + +我们首先考虑 Alice 移除余数为 $1$ 的石子的情况。如果 Alice 移除了一个余数为 $1$ 的石子,石子 $0$ 对石子价值总和对 $3$ 的余数不会改变,因此价值对 $3$ 的余数为 $0$ 的石子可以在任意回合被移除,我们暂时不考虑。所以 Bob 也只能移除余数为 $1$ 的石子,之后 Alice 移除余数为 $2$ 的石子,依次进行,序列为 $1, 1, 2, 1, 2, \ldots$。在这种情况下,如果最终回合数为奇数,且还有剩余石子,那么 Alice 获胜,否则 Bob 获胜。 + +对于第一回合 Alice 移除余数为 $2$ 的石子的情况,我们可以得到类似的结论。 + +时间复杂度 $O(n)$,其中 $n$ 是数组 $\textit{stones}$ 的长度。空间复杂度 $O(1)$。 @@ -93,21 +105,21 @@ Alice 输掉游戏,因为已移除石子值总和(15)可以被 3 整除, ```python class Solution: def stoneGameIX(self, stones: List[int]) -> bool: - def check(c): - if c[1] == 0: + def check(cnt: List[int]) -> bool: + if cnt[1] == 0: return False - c[1] -= 1 - turn = 1 + min(c[1], c[2]) * 2 + c[0] - if c[1] > c[2]: - turn += 1 - c[1] -= 1 - return turn % 2 == 1 and c[1] != c[2] - - c = [0] * 3 - for s in stones: - c[s % 3] += 1 - c1 = [c[0], c[2], c[1]] - return check(c) or check(c1) + cnt[1] -= 1 + r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0] + if cnt[1] > cnt[2]: + cnt[1] -= 1 + r += 1 + return r % 2 == 1 and cnt[1] != cnt[2] + + c1 = [0] * 3 + for x in stones: + c1[x % 3] += 1 + c2 = [c1[0], c1[2], c1[1]] + return check(c1) or check(c2) ``` #### Java @@ -115,25 +127,24 @@ class Solution: ```java class Solution { public boolean stoneGameIX(int[] stones) { - int[] c = new int[3]; - for (int s : stones) { - ++c[s % 3]; + int[] c1 = new int[3]; + for (int x : stones) { + c1[x % 3]++; } - int[] t = new int[] {c[0], c[2], c[1]}; - return check(c) || check(t); + int[] c2 = {c1[0], c1[2], c1[1]}; + return check(c1) || check(c2); } - private boolean check(int[] c) { - if (c[1] == 0) { + private boolean check(int[] cnt) { + if (--cnt[1] < 0) { return false; } - --c[1]; - int turn = 1 + Math.min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + int r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; } - return turn % 2 == 1 && c[1] != c[2]; + return r % 2 == 1 && cnt[1] != cnt[2]; } } ``` @@ -144,21 +155,23 @@ class Solution { class Solution { public: bool stoneGameIX(vector& stones) { - vector c(3); - for (int s : stones) ++c[s % 3]; - vector t = {c[0], c[2], c[1]}; - return check(c) || check(t); - } - - bool check(vector& c) { - if (c[1] == 0) return false; - --c[1]; - int turn = 1 + min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + vector c1(3); + for (int x : stones) { + ++c1[x % 3]; } - return turn % 2 == 1 && c[1] != c[2]; + vector c2 = {c1[0], c1[2], c1[1]}; + auto check = [](auto& cnt) -> bool { + if (--cnt[1] < 0) { + return false; + } + int r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 && cnt[1] != cnt[2]; + }; + return check(c1) || check(c2); } }; ``` @@ -167,23 +180,48 @@ public: ```go func stoneGameIX(stones []int) bool { - check := func(c [3]int) bool { - if c[1] == 0 { + c1 := [3]int{} + for _, x := range stones { + c1[x%3]++ + } + c2 := [3]int{c1[0], c1[2], c1[1]} + check := func(cnt [3]int) bool { + if cnt[1] == 0 { return false } - c[1]-- - turn := 1 + min(c[1], c[2])*2 + c[0] - if c[1] > c[2] { - c[1]-- - turn++ + cnt[1]-- + r := 1 + min(cnt[1], cnt[2])*2 + cnt[0] + if cnt[1] > cnt[2] { + cnt[1]-- + r++ } - return turn%2 == 1 && c[1] != c[2] + return r%2 == 1 && cnt[1] != cnt[2] } - c := [3]int{} - for _, s := range stones { - c[s%3]++ - } - return check(c) || check([3]int{c[0], c[2], c[1]}) + return check(c1) || check(c2) +} +``` + +#### TypeScript + +```ts +function stoneGameIX(stones: number[]): boolean { + const c1: number[] = Array(3).fill(0); + for (const x of stones) { + ++c1[x % 3]; + } + const c2: number[] = [c1[0], c1[2], c1[1]]; + const check = (cnt: number[]): boolean => { + if (--cnt[1] < 0) { + return false; + } + let r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 === 1 && cnt[1] !== cnt[2]; + }; + return check(c1) || check(c2); } ``` diff --git a/solution/2000-2099/2029.Stone Game IX/README_EN.md b/solution/2000-2099/2029.Stone Game IX/README_EN.md index f9c7e94f2768f..43242fed1ec31 100644 --- a/solution/2000-2099/2029.Stone Game IX/README_EN.md +++ b/solution/2000-2099/2029.Stone Game IX/README_EN.md @@ -77,7 +77,19 @@ Alice loses the game because the sum of the removed stones (15) is divisible by -### Solution 1 +### Solution 1: Greedy + Case Discussion + +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$. + +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$. + +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$. + +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. + +For the case where Alice removes a stone with a remainder of $2$ in the first round, we can draw a similar conclusion. + +The time complexity is $O(n)$, where $n$ is the length of the array $\textit{stones}$. The space complexity is $O(1)$. @@ -86,21 +98,21 @@ Alice loses the game because the sum of the removed stones (15) is divisible by ```python class Solution: def stoneGameIX(self, stones: List[int]) -> bool: - def check(c): - if c[1] == 0: + def check(cnt: List[int]) -> bool: + if cnt[1] == 0: return False - c[1] -= 1 - turn = 1 + min(c[1], c[2]) * 2 + c[0] - if c[1] > c[2]: - turn += 1 - c[1] -= 1 - return turn % 2 == 1 and c[1] != c[2] - - c = [0] * 3 - for s in stones: - c[s % 3] += 1 - c1 = [c[0], c[2], c[1]] - return check(c) or check(c1) + cnt[1] -= 1 + r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0] + if cnt[1] > cnt[2]: + cnt[1] -= 1 + r += 1 + return r % 2 == 1 and cnt[1] != cnt[2] + + c1 = [0] * 3 + for x in stones: + c1[x % 3] += 1 + c2 = [c1[0], c1[2], c1[1]] + return check(c1) or check(c2) ``` #### Java @@ -108,25 +120,24 @@ class Solution: ```java class Solution { public boolean stoneGameIX(int[] stones) { - int[] c = new int[3]; - for (int s : stones) { - ++c[s % 3]; + int[] c1 = new int[3]; + for (int x : stones) { + c1[x % 3]++; } - int[] t = new int[] {c[0], c[2], c[1]}; - return check(c) || check(t); + int[] c2 = {c1[0], c1[2], c1[1]}; + return check(c1) || check(c2); } - private boolean check(int[] c) { - if (c[1] == 0) { + private boolean check(int[] cnt) { + if (--cnt[1] < 0) { return false; } - --c[1]; - int turn = 1 + Math.min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + int r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; } - return turn % 2 == 1 && c[1] != c[2]; + return r % 2 == 1 && cnt[1] != cnt[2]; } } ``` @@ -137,21 +148,23 @@ class Solution { class Solution { public: bool stoneGameIX(vector& stones) { - vector c(3); - for (int s : stones) ++c[s % 3]; - vector t = {c[0], c[2], c[1]}; - return check(c) || check(t); - } - - bool check(vector& c) { - if (c[1] == 0) return false; - --c[1]; - int turn = 1 + min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + vector c1(3); + for (int x : stones) { + ++c1[x % 3]; } - return turn % 2 == 1 && c[1] != c[2]; + vector c2 = {c1[0], c1[2], c1[1]}; + auto check = [](auto& cnt) -> bool { + if (--cnt[1] < 0) { + return false; + } + int r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 && cnt[1] != cnt[2]; + }; + return check(c1) || check(c2); } }; ``` @@ -160,23 +173,48 @@ public: ```go func stoneGameIX(stones []int) bool { - check := func(c [3]int) bool { - if c[1] == 0 { + c1 := [3]int{} + for _, x := range stones { + c1[x%3]++ + } + c2 := [3]int{c1[0], c1[2], c1[1]} + check := func(cnt [3]int) bool { + if cnt[1] == 0 { return false } - c[1]-- - turn := 1 + min(c[1], c[2])*2 + c[0] - if c[1] > c[2] { - c[1]-- - turn++ + cnt[1]-- + r := 1 + min(cnt[1], cnt[2])*2 + cnt[0] + if cnt[1] > cnt[2] { + cnt[1]-- + r++ } - return turn%2 == 1 && c[1] != c[2] + return r%2 == 1 && cnt[1] != cnt[2] } - c := [3]int{} - for _, s := range stones { - c[s%3]++ - } - return check(c) || check([3]int{c[0], c[2], c[1]}) + return check(c1) || check(c2) +} +``` + +#### TypeScript + +```ts +function stoneGameIX(stones: number[]): boolean { + const c1: number[] = Array(3).fill(0); + for (const x of stones) { + ++c1[x % 3]; + } + const c2: number[] = [c1[0], c1[2], c1[1]]; + const check = (cnt: number[]): boolean => { + if (--cnt[1] < 0) { + return false; + } + let r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 === 1 && cnt[1] !== cnt[2]; + }; + return check(c1) || check(c2); } ``` diff --git a/solution/2000-2099/2029.Stone Game IX/Solution.cpp b/solution/2000-2099/2029.Stone Game IX/Solution.cpp index e79b87c2d530d..5aa5d47d144df 100644 --- a/solution/2000-2099/2029.Stone Game IX/Solution.cpp +++ b/solution/2000-2099/2029.Stone Game IX/Solution.cpp @@ -1,20 +1,22 @@ class Solution { public: bool stoneGameIX(vector& stones) { - vector c(3); - for (int s : stones) ++c[s % 3]; - vector t = {c[0], c[2], c[1]}; - return check(c) || check(t); - } - - bool check(vector& c) { - if (c[1] == 0) return false; - --c[1]; - int turn = 1 + min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + vector c1(3); + for (int x : stones) { + ++c1[x % 3]; } - return turn % 2 == 1 && c[1] != c[2]; + vector c2 = {c1[0], c1[2], c1[1]}; + auto check = [](auto& cnt) -> bool { + if (--cnt[1] < 0) { + return false; + } + int r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 && cnt[1] != cnt[2]; + }; + return check(c1) || check(c2); } }; \ No newline at end of file diff --git a/solution/2000-2099/2029.Stone Game IX/Solution.go b/solution/2000-2099/2029.Stone Game IX/Solution.go index 81eb947996d98..4099e7bdc34e7 100644 --- a/solution/2000-2099/2029.Stone Game IX/Solution.go +++ b/solution/2000-2099/2029.Stone Game IX/Solution.go @@ -1,19 +1,20 @@ func stoneGameIX(stones []int) bool { - check := func(c [3]int) bool { - if c[1] == 0 { + c1 := [3]int{} + for _, x := range stones { + c1[x%3]++ + } + c2 := [3]int{c1[0], c1[2], c1[1]} + check := func(cnt [3]int) bool { + if cnt[1] == 0 { return false } - c[1]-- - turn := 1 + min(c[1], c[2])*2 + c[0] - if c[1] > c[2] { - c[1]-- - turn++ + cnt[1]-- + r := 1 + min(cnt[1], cnt[2])*2 + cnt[0] + if cnt[1] > cnt[2] { + cnt[1]-- + r++ } - return turn%2 == 1 && c[1] != c[2] - } - c := [3]int{} - for _, s := range stones { - c[s%3]++ + return r%2 == 1 && cnt[1] != cnt[2] } - return check(c) || check([3]int{c[0], c[2], c[1]}) + return check(c1) || check(c2) } \ No newline at end of file diff --git a/solution/2000-2099/2029.Stone Game IX/Solution.java b/solution/2000-2099/2029.Stone Game IX/Solution.java index 38f3f8f0a94f1..3e2318e28964e 100644 --- a/solution/2000-2099/2029.Stone Game IX/Solution.java +++ b/solution/2000-2099/2029.Stone Game IX/Solution.java @@ -1,23 +1,22 @@ class Solution { public boolean stoneGameIX(int[] stones) { - int[] c = new int[3]; - for (int s : stones) { - ++c[s % 3]; + int[] c1 = new int[3]; + for (int x : stones) { + c1[x % 3]++; } - int[] t = new int[] {c[0], c[2], c[1]}; - return check(c) || check(t); + int[] c2 = {c1[0], c1[2], c1[1]}; + return check(c1) || check(c2); } - private boolean check(int[] c) { - if (c[1] == 0) { + private boolean check(int[] cnt) { + if (--cnt[1] < 0) { return false; } - --c[1]; - int turn = 1 + Math.min(c[1], c[2]) * 2 + c[0]; - if (c[1] > c[2]) { - --c[1]; - ++turn; + int r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; } - return turn % 2 == 1 && c[1] != c[2]; + return r % 2 == 1 && cnt[1] != cnt[2]; } } \ No newline at end of file diff --git a/solution/2000-2099/2029.Stone Game IX/Solution.py b/solution/2000-2099/2029.Stone Game IX/Solution.py index 60df6a1d355b8..b566b452d225d 100644 --- a/solution/2000-2099/2029.Stone Game IX/Solution.py +++ b/solution/2000-2099/2029.Stone Game IX/Solution.py @@ -1,17 +1,17 @@ class Solution: def stoneGameIX(self, stones: List[int]) -> bool: - def check(c): - if c[1] == 0: + def check(cnt: List[int]) -> bool: + if cnt[1] == 0: return False - c[1] -= 1 - turn = 1 + min(c[1], c[2]) * 2 + c[0] - if c[1] > c[2]: - turn += 1 - c[1] -= 1 - return turn % 2 == 1 and c[1] != c[2] + cnt[1] -= 1 + r = 1 + min(cnt[1], cnt[2]) * 2 + cnt[0] + if cnt[1] > cnt[2]: + cnt[1] -= 1 + r += 1 + return r % 2 == 1 and cnt[1] != cnt[2] - c = [0] * 3 - for s in stones: - c[s % 3] += 1 - c1 = [c[0], c[2], c[1]] - return check(c) or check(c1) + c1 = [0] * 3 + for x in stones: + c1[x % 3] += 1 + c2 = [c1[0], c1[2], c1[1]] + return check(c1) or check(c2) diff --git a/solution/2000-2099/2029.Stone Game IX/Solution.ts b/solution/2000-2099/2029.Stone Game IX/Solution.ts new file mode 100644 index 0000000000000..526a11522b5bd --- /dev/null +++ b/solution/2000-2099/2029.Stone Game IX/Solution.ts @@ -0,0 +1,19 @@ +function stoneGameIX(stones: number[]): boolean { + const c1: number[] = Array(3).fill(0); + for (const x of stones) { + ++c1[x % 3]; + } + const c2: number[] = [c1[0], c1[2], c1[1]]; + const check = (cnt: number[]): boolean => { + if (--cnt[1] < 0) { + return false; + } + let r = 1 + Math.min(cnt[1], cnt[2]) * 2 + cnt[0]; + if (cnt[1] > cnt[2]) { + --cnt[1]; + ++r; + } + return r % 2 === 1 && cnt[1] !== cnt[2]; + }; + return check(c1) || check(c2); +}