LeetCodeAnimation/notes/LeetCode第877号问题:石子游戏.md
2019-05-02 16:23:13 +08:00

126 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LeetCode 第 877 号问题:石子游戏
> 本文首发于公众号「五分钟学算法」,是[图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>)系列文章之一。
>
> 个人网站:[https://www.cxyxiaowu.com](https://www.cxyxiaowu.com)
### 题目描述
喜羊羊和灰太狼用几堆石子在做游戏。偶数堆石子**排成一行**,每堆都有正整数颗石子 `piles[i]`
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
喜羊羊和灰太狼轮流进行,喜羊羊先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设喜羊羊和灰太狼都发挥出最佳水平,当喜羊羊赢得比赛时返回 `true` ,当灰太狼赢得比赛时返回 `false`
### 题目分析
举两个例子来帮助理解题意。
#### 例子一:
输入:[ 5345 ]
输出true
**解释**
喜羊羊先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [ 3 45 ] 。
如果灰太狼拿走前 3 颗,那么剩下的是 [ 45 ],喜羊羊拿走后 5 颗赢得 10 分。
如果灰太狼拿走后 5 颗,那么剩下的是 [ 34 ],喜羊羊拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对喜羊羊来说是一个胜利的举动,所以我们返回 true 。
#### 例子二:
输入:[ 51000023 ]
输出true
**解释**
喜羊羊先开始,只能拿前 5 颗或后 3 颗石子 。
假设他取了后 3 颗,这一行就变成了 [ 5100002 ]。
灰太狼肯定会在剩下的这一行中取走前 5 颗,这一行就变成了 [ 100002 ]。
然后喜羊羊取走前 10000 颗,总共赢得 10003 分,灰太狼赢得 7 分。
这表明,取后 3 颗石子对喜羊羊来说是一个胜利的举动,所以我们返回 true 。
**这个例子表明,并不是需要每次都挑选最大的那堆石头**
### 题目回答
涉及到最优解的问题,那么肯定要去尝试一下使用 **动态规划 **来解决了。
先看一下力扣的正规题解:
让我们改变游戏规则,使得每当灰太狼得分时,都会从喜羊羊的分数中扣除。
`dp(i, j)` 为喜羊羊可以获得的最大分数,其中剩下的堆中的石子数是 `piles[i], piles[i+1], ..., piles[j]`。这在比分游戏中很自然:我们想知道游戏中每个位置的值。
我们可以根据 `dp(i + 1j)``dp(ij-1)` 来制定 `dp(ij)` 的递归我们可以使用动态编程以不重复这个递归中的工作。该方法可以输出正确的答案因为状态形成一个DAG有向无环图
当剩下的堆的石子数是 `piles[i], piles[i+1], ..., piles[j]` 时,轮到的玩家最多有 2 种行为。
可以通过比较 `j-i``N modulo 2` 来找出轮到的人。
如果玩家是喜羊羊,那么它将取走 `piles[i]``piles[j]` 颗石子,增加它的分数。之后,总分为 `piles[i] + dp(i+1, j)``piles[j] + dp(i, j-1)`;我们想要其中的最大可能得分。
如果玩家是灰太狼,那么它将取走 `piles[i]``piles[j]` 颗石子,减少喜羊羊这一数量的分数。之后,总分为 `-piles[i] + dp(i+1, j)``-piles[j] + dp(i, j-1)`;我们想要其中的最小可能得分。
代码如下:
![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/20190502124645.jpg)
上面的代码并不算复杂,当然,如果你看不懂也没关系,不影响解决问题,请看下面的数学分析。
### 数学分析
因为石头的数量是奇数,因此只有两种结果,输或者赢。
喜羊羊先开始拿石头,随便拿!然后比较石头数量:
1. 如果石头数量多于对手,赢了;
2. 如果石头数量少于对手,自己拿石头的顺序和对手拿石头的顺序对调,还是赢。
所以代码如下:
```java
class Solution {
public boolean stoneGame(int[] piles) {
return true;
}
}
```
看完之后,你的心情是怎么样的?
此题的LeetCode 的评论区里一片吐槽:**这是什么沙雕题目!**
可能搞过 ACM 等竞赛的人都会微微一笑:不会几万个套路怎么好意思说自己是 acmer 。我们这些普通人为之惊奇的题目,到他们这里就是彻底被玩坏了,各种稀奇古怪的秒解。
![](https://bucket-1257126549.cos.ap-guangzhou.myqcloud.com/blog/fz0rq.png)