这道算法题其实并不难,若是你把文章从头至尾看完的话基本上能看懂,但若是你看到最后的话大几率会说一句:这是什么沙雕题目?!java
题目来源于 LeetCode 第 877 号问题:石子游戏。算法
为了更好理解,我改编了一下题目,描述是这样的:编程
喜羊羊和灰太狼用几堆石子在作游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i]
。spa
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,因此没有平局。code
喜羊羊和灰太狼轮流进行,喜羊羊先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种状况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。cdn
假设喜羊羊和灰太狼都发挥出最佳水平,当喜羊羊赢得比赛时返回 true
,当灰太狼赢得比赛时返回 false
。blog
举两个例子来帮助理解题意。递归
输入:[ 5,3,4,5 ]游戏
输出:true数学
解释:
喜羊羊先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [ 3 ,4,5 ] 。
若是灰太狼拿走前 3 颗,那么剩下的是 [ 4,5 ],喜羊羊拿走后 5 颗赢得 10 分。
若是灰太狼拿走后 5 颗,那么剩下的是 [ 3,4 ],喜羊羊拿走后 4 颗赢得 9 分。
这代表,取前 5 颗石子对喜羊羊来讲是一个胜利的举动,因此咱们返回 true 。
输入:[ 5,10000,2,3 ]
输出:true
解释:
喜羊羊先开始,只能拿前 5 颗或后 3 颗石子 。
假设他取了后 3 颗,这一行就变成了 [ 5,10000,2 ]。
灰太狼确定会在剩下的这一行中取走前 5 颗,这一行就变成了 [ 10000,2 ]。
而后喜羊羊取走前 10000 颗,总双赢得 10003 分,灰太狼赢得 7 分。
这代表,取后 3 颗石子对喜羊羊来讲是一个胜利的举动,因此咱们返回 true 。
这个例子代表,并非须要每次都挑选最大的那堆石头。
涉及到最优解的问题,那么确定要去尝试一下使用 **动态规划 **来解决了。
先看一下力扣的正规题解:
让咱们改变游戏规则,使得每当灰太狼得分时,都会从喜羊羊的分数中扣除。
令 dp(i, j)
为喜羊羊能够得到的最大分数,其中剩下的堆中的石子数是 piles[i], piles[i+1], ..., piles[j]
。这在比分游戏中很天然:咱们想知道游戏中每一个位置的值。
咱们能够根据 dp(i + 1,j)
和 dp(i,j-1)
来制定 dp(i,j)
的递归,咱们可使用动态编程以不重复这个递归中的工做。(该方法能够输出正确的答案,由于状态造成一个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)
;咱们想要其中的最小可能得分。
代码以下:
上面的代码并不算复杂,固然,若是你看不懂也不要紧,不影响解决问题,请看下面的数学分析。
由于石头的数量是奇数,所以只有两种结果,输或者赢。
喜羊羊先开始拿石头,随便拿!而后比较石头数量:
因此代码以下:
class Solution {
public boolean stoneGame(int[] piles) {
return true;
}
}
复制代码
看完以后,你的心情是怎么样的?
此题的LeetCode 的评论区里一片吐槽:这是什么沙雕题目!
可能搞过 ACM 等竞赛的人都会微微一笑:不会几万个套路怎么好意思说本身是 acmer 。咱们这些普通人为之惊奇的题目,到他们这里就是完全被玩坏了,各类稀奇古怪的秒解。