[LeetCode] 877. Stone Game 石子游戏
Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones `piles[i]`.
The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties.
Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins.
Assuming Alex and Lee play optimally, return True if and only if Alex wins the game.
Example 1:
Input: [5,3,4,5]
Output: true
Explanation:
Alex starts first, and can only take the first 5 or the last 5.
Say he takes the first 5, so that the row becomes [3, 4, 5].
If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points.
If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points.
This demonstrated that taking the first 5 was a winning move for Alex, so we return true.
Note:
2 <= piles.length <= 500piles.lengthis even.1 <= piles[i] <= 500sum(piles)is odd.
这道题说是有偶数堆的石子,每堆的石子个数可能不同,但石子总数是奇数个。现在 Alex 和 Lee (不应该是 Alice 和 Bob 么??)两个人轮流选石子堆,规则是每次只能选开头和末尾中的一堆,最终获得石子总数多的人获胜。若 Alex 先选,两个人都会一直做最优选择,问我们最终 Alex 是否能获胜。博主最先想到的方法是像 [Predict the Winner](http://www.cnblogs.com/grandyang/p/6369688.html) 中的那样,用个 player 变量来记录当前是哪个玩家在操作,若为0,表示 Alex 在选,那么他只有两种选择,要么拿首堆,要么拿尾堆,两种情况分别调用递归,两个递归函数只要有一个能返回 true,则表示 Alex 可以获胜,还需要用个变量 cur0 来记录当前 Alex 的石子总数。同理,若 Lee 在选,即 player 为1的时候,也是只有两种选择,分别调用递归,两个递归函数只要有一个能返回 true,则表示 Lee 可以获胜,用 cur1 来记录当前 Lee 的石子总数。需要注意的是,当首堆或尾堆被选走了后,我们需要标记,这里就有两种方法,一种是从原 piles 中删除选走的堆(或者是新建一个不包含选走堆的数组),但是这种方法会包括大量的拷贝运算,无法通过 OJ。另一种方法是用两个指针 left 和 right,分别指向首尾的位置。当选取了首堆时,则 left 自增1,若选了尾堆时,则 right 自减1。这样就不用执行删除操作,或是拷贝数组了,大大的提高了运行效率,参见代码如下:
解法一:
class Solution {
public:
bool stoneGame(vector<int>& piles) {
return helper(piles, 0, 0, 0, (int)piles.size() - 1, 0);
}
bool helper(vector<int>& piles, int cur0, int cur1, int left, int right, int player) {
if (left > right) return cur0 > cur1;
if (player == 0) {
return helper(piles, cur0 + piles[left], cur1, left + 1, right, 1) || helper(piles, cur0 + piles[right], cur1, left + 1, right, 1);
} else {
return helper(piles, cur0, cur1 + piles[left], left, right - 1, 0) || helper(piles, cur0, cur1 + piles[right], left, right - 1, 0);
}
}
};
这道题也可以使用动态规划 Dynamic Programming 来做,由于玩家获胜的规则是拿到的石子数多,那么多的石子数就可以量化为 dp 值。所以我们用一个二维数组,其中 dp[i][j] 表示在区间 [i, j] 内 Alex 比 Lee 多拿的石子数,若为正数,说明 Alex 拿得多,若为负数,则表示 Lee 拿得多。则最终只要看 dp[0][n-1] 的值,若为正数,则 Alex 能获胜。现在就要找状态转移方程了,我们想,在区间 [i, j] 内要计算 Alex 比 Lee 多拿的石子数,在这个区间内,Alex 只能拿i或者j位置上的石子,那么当 Alex 拿了 piles[i] 的话,等于 Alex 多了 piles[i] 个石子,此时区间缩小成了 [i+1, j],此时应该 Lee 拿了,此时根据我们以往的 DP 经验,应该调用子区间的 dp 值,没错,但这里 dp[i+1][j] 表示是在区间 [i+1, j] 内 Alex 多拿的石子数,但是若区间 [i+1, j] 内 Lee 先拿的话,其多拿的石子数也应该是 dp[i+1][j],因为两个人都要最优化拿,那么 dp[i][j] 的值其实可以被 piles[i] - dp[i+1][j] 更新,因为 Alex 拿了 piles[i],减去 Lee 多出的 dp[i+1][j],就是区间 [i, j] 中 Alex 多拿的石子数。同理,假如 Alex 先拿 piles[j],那么就用 piles[j] - dp[i][j-1] 来更新 dp[i][j],则我们用二者的较大值来更新即可。注意开始的时候要把 dp[i][i] 都初始化为 piles[i],还需要注意的是,这里的更新顺序很重要,是从小区间开始更新,在之前那道 [Burst Balloons](http://www.cnblogs.com/grandyang/p/5006441.html),博主详细的讲了这种 dp 的更新顺序,可以去看看,参见代码如下:
解法二:
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int n = piles.size();
vector<vector<int>> dp(n, vector<int>(n));
for (int i = 0; i < n; ++i) dp[i][i] = piles[i];
for (int len = 1; len < n; ++len) {
for (int i = 0; i < n - len; ++i) {
int j = i + len;
dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);
}
}
return dp[0][n - 1] > 0;
}
};
其实这道题是一道脑筋急转弯题,跟之前那道 [Nim Game](http://www.cnblogs.com/grandyang/p/4873248.html) 有些像。原因就在于题目中的一个条件,那就是总共有偶数堆,那么就是可以分为堆数相等的两堆,比如我们按奇偶分为两堆。题目还说了石子总数为奇数个,那么分出的这两堆的石子总数一定是不相等的,那么我们只要每次一直取石子总数多的奇数堆或者偶数堆,Alex 就一定可以躺赢,所以最叼的方法就是直接返回 true。我。。我。。我王司徒。。表示不服。。。我从未见过如此。。机智过人。。的解法~
解法三:
class Solution {
public:
bool stoneGame(vector<int>& piles) {
return true;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/877
类似题目:
Guess Number Higher or Lower II
参考资料:
https://leetcode.com/problems/stone-game/
https://leetcode.com/problems/stone-game/discuss/154610/DP-or-Just-return-true
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 877. Stone Game 石子游戏的更多相关文章
- leetcode 877. Stone Game 详解 -——动态规划
原博客地址 https://blog.csdn.net/androidchanhao/article/details/81271077 题目链接 https://leetcode.com/proble ...
- LeetCode 877. Stone Game
原题链接在这里:https://leetcode.com/problems/stone-game/ 题目: Alex and Lee play a game with piles of stones. ...
- [LeetCode] 877. Stone Game == [LintCode] 396. Coins in a Line 3_hard tag: 区间Dynamic Programming, 博弈
Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, ...
- Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game)
Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game) 亚历克斯和李用几堆石子在做游戏.偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] . 游戏以谁手中的石子最 ...
- 877. Stone Game - LeetCode
Question 877. Stone Game Solution 题目大意: 说有偶数个数字,alex和lee两个人比赛,每次轮流从第一个数字或最后一个数字中拿走一个(偶数个数字,所以他俩拿的数字个 ...
- bzoj2000 [Hnoi2010]stone 取石头游戏
Description A 公司正在举办一个智力双人游戏比赛----取石子游戏,游戏的获胜者将会获得 A 公司提供的丰厚奖金,因此吸引了来自全国各地的许多聪明的选手前来参加比赛. 与经典的取石子游戏相 ...
- LeetCode 1140. Stone Game II
原题链接在这里:https://leetcode.com/problems/stone-game-ii/ 题目: Alex and Lee continue their games with pile ...
- BZOJ 1115: [POI2009]石子游戏Kam
1115: [POI2009]石子游戏Kam Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 883 Solved: 545[Submit][Stat ...
- Games:取石子游戏(POJ 1067)
取石子游戏 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 37662 Accepted: 12594 Descripti ...
随机推荐
- 送书『构建Apache Kafka流数据应用』和『小灰的算法之旅』和『Java并发编程的艺术』
读书好处 1.可以使我们增长见识. 2.可提高我们的阅读能力和写作水平. 3.可以使我们变的有修养. 4.可以使我们找到好工作. 5.可以使我们在竞争激烈的社会立于不败之地. 6.最大的好处是可以让你 ...
- docker命令之link
1.新建两台容器,第二台(busybox_2)link到第一台(busybox_1) [root@localhost ~]# docker run -d -it --name busybox_1 bu ...
- sso单点登录的入门(Session跨域、Spring-Session共享)
1.单点登录,就是多系统,单一位置登录,实现多系统同时登录的一种技术.单点登录一般是用于互相授信的系统,实现单一位置登录,全系统有效的. 区分与三方登录(第三方登录) ,三方登录:某系统,使用其他系统 ...
- WebApi接口安全性 接口权限调用、参数防篡改防止恶意调用
背景介绍 最近使用WebApi开发一套对外接口,主要是数据的外送以及结果回传,接口没什么难度,采用WebApi+EF的架构简单创建一个模板工程,使用template生成一套WebApi接口,去掉put ...
- easyui datagird 解决行高不一致问题!
<style>.datagrid-btable .datagrid-cell {padding: 6px 4px;overflow: hidden;text-overflow: ellip ...
- centos6.8下hadoop3.1.1完全分布式安装指南
前述:这篇文档是建立在三台虚拟机相互ping通,防火墙关闭,hosts文件修改,SSH 免密码登录,主机名修改等的基础上开始的. 一.传入文件 1.创建安装目录 mkdir /usr/local/so ...
- VMware虚拟机可与Win10物理机互ping并可访问互联网的设置方法
一.系统环境: VMware 15.Windows 10 1903.Windows 7 虚拟机 二.具体步骤: 1.Win10物理机,控制面板-所有控制面板项-网络连接-物理网卡适配器右键-属性-共 ...
- odoo10学习笔记十六:定时任务
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11189382.html 一:定义定时器数据模型 模型中定义需要用到的字段.定时方法 from odoo im ...
- Centos7服务器搭建网络家园和论坛
环境:Centos7 工具:mysql,php,httpd 目的:熟练掌握httpd服务器搭建和个服务器之间的配合. 有兴趣的朋友可以来实践一下,我会提供各种源码进行搭建. 网络家园和论坛源码:htt ...
- Ubuntu 18.04通过命令禁用/开启触控板
Ubuntu下经常遇到无法用快捷键关闭触控板的情况,博主的电脑安装Ubuntu18.04后便出现了该问题. 解决办法: 首先查看输入设备的id,命令行输入: xinput ,插鼠标与不插鼠标时,Tou ...