作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/stone-game/description/

题目描述

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:

  1. 2 <= piles.length <= 500
  2. piles.length is even.
  3. 1 <= piles[i] <= 500
  4. sum(piles) is odd.

题目大意

有一个数组,两人玩游戏,可以从这个数组的开头或者结尾选择一个数字拿走,拿走以后,另一个人可以继续拿。问,先拿的那个人是否会赢。

解题方法

数学

直接return True就行。因为题目给了限定条件,总和是奇数,数字的个数是偶数。这样也就是简化成了问第一个人拿到的数字总和能否超过sum/2.

所以,第一个人直接选择偶数位置或者奇数位置的数字可以。

比如Alex选择偶数,piles[0], piles[2], …, piles[n-2],
他选择了piles[0],这个时候Lee可以选择piles[1] 或 piles[n - 1].
之后Alex可以继续选择偶数的位置。所以Lee就被迫选择了所有奇数的位置。

反之,如果Alex从倒数第一个开始选,那么他能选到所有的奇数位置,Lee被迫选偶数位置。

故,Alex只要选出奇数、偶数位置中求和之后最大的就行,一定会赢。

class Solution:
def stoneGame(self, piles):
return True

双函数

使用递归求解。这个解法是左程云的算法讲解。

思路就是,作为先选的人,要选择从前面选和从后面选两种方案中的最大值。
作为后选的人,要选择前面选和从后面选两种方案中的最小值。

alex是先选的,所以调用f函数判断他能否赢。

直接递归超时,所以我是用了记忆化搜索减少了时间,就能通过了。

代码如下:

class Solution(object):

    def stoneGame(self, piles):
"""
:type piles: List[int]
:rtype: bool
"""
if not piles:
return False
self.F = [[0 for i in range(len(piles))] for j in range(len(piles))]
self.S = [[0 for i in range(len(piles))] for j in range(len(piles))]
_sum = sum(piles)
alex = self.f(piles, 0, len(piles) - 1)
return alex > _sum / 2 def f(self, piles, i, j):
"""
先选
"""
if i == j:
return piles[i]
if self.F[i][j] != 0:
return self.F[i][j]
curr = max(piles[i] + self.s(piles, i + 1, j), piles[j] + self.s(piles, i, j - 1))
self.F[i][j] = curr
return curr def s(self, piles, i, j):
"""
后选
"""
if i == j:
return 0
if self.S[i][j] != 0:
return self.S[i][j]
curr = min(self.f(piles, i + 1, j), self.f(piles, i, j - 1))
self.S[i][j] = curr
return curr

使用map来完成记忆化搜索,也能通过:

class Solution(object):

    def stoneGame(self, piles):
"""
:type piles: List[int]
:rtype: bool
"""
self.f_map, self.s_map = dict(), dict()
_sum = sum(piles)
alex = self.f(piles, 0, len(piles)-1)
print(alex, _sum)
return alex > _sum / 2.0 def f(self, piles, start, end):
if start == end:
return piles[start]
if (start, end) not in self.f_map:
f_val = max(piles[start] + self.s(piles, start+1, end), piles[end] + self.s(piles, start, end-1))
self.f_map[(start, end)] = f_val
return self.f_map[(start, end)] def s(self, piles, start, end):
if start == end:
return 0
if (start, end) not in self.s_map:
s_val = min(self.f(piles, start+1, end), self.f(piles, start, end-1))
self.s_map[(start, end)] = s_val
return self.s_map[(start, end)]

单函数 + 记忆化递归

使用score函数表示Alex能比Lee多选的分数。可能比双函数更简洁易懂了。

记忆化递归的缺点:1.有可能爆栈;2.无法降维,而DP是可以降维的。

我写的是cpp代码:

class Solution {
public:
bool stoneGame(vector<int>& piles) {
const int N = piles.size();
m_ = vector<vector<int>>(N, vector<int>(N, INT_MIN));
return score(piles, 0, N - 1) > 0;
}
private:
vector<vector<int>> m_;
//Alex比Lee多的分数
int score(vector<int>& piles, int l, int r) {
if (l == r) return piles[l];
if (m_[l][r] == INT_MIN) {
m_[l][r] = max(piles[l] - score(piles, l + 1, r),
piles[r] - score(piles, l, r - 1));
}
return m_[l][r];
}
};

动态规划

动态规划解法比较难想,dp数组的第i个位置表示的是从第i个石头到第i+l-1个石头之间最大的比对手得分。

使用的是一个长度变量和起始索引,计算每个位置开始的长度1~N长度的区间的dp状态。

class Solution {
public:
bool stoneGame(vector<int>& piles) {
const int N = piles.size();
// dp[i] := max(your_stones - op_stones) for piles[i] to piles[i + l - 1]
vector<vector<int>> dp(N, vector<int>(N, INT_MIN));
for (int i = 0; i < N; i++)
dp[i][i] = piles[i];
for (int l = 2; l <= N; l++) {
for (int i = 0; i < N - l + 1; i++) {
int j = i + l - 1;
dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);
}
}
return dp[0][N - 1] > 0;
}
};

参考资料:

https://leetcode.com/problems/stone-game/discuss/154610/C++JavaPython-DP-or-Just-return-true

日期

2018 年 9 月 4 日 —— 迎接明媚的阳光!
2018 年 12 月 4 日 —— 周二啦!

【LeetCode】877. Stone Game 解题报告(Python & C++)的更多相关文章

  1. 【LeetCode】120. Triangle 解题报告(Python)

    [LeetCode]120. Triangle 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址htt ...

  2. LeetCode 1 Two Sum 解题报告

    LeetCode 1 Two Sum 解题报告 偶然间听见leetcode这个平台,这里面题量也不是很多200多题,打算平时有空在研究生期间就刷完,跟跟多的练习算法的人进行交流思想,一定的ACM算法积 ...

  3. 【LeetCode】Permutations II 解题报告

    [题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...

  4. 【LeetCode】Island Perimeter 解题报告

    [LeetCode]Island Perimeter 解题报告 [LeetCode] https://leetcode.com/problems/island-perimeter/ Total Acc ...

  5. 【LeetCode】01 Matrix 解题报告

    [LeetCode]01 Matrix 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/01-matrix/#/descripti ...

  6. 【LeetCode】Largest Number 解题报告

    [LeetCode]Largest Number 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/largest-number/# ...

  7. 【LeetCode】Gas Station 解题报告

    [LeetCode]Gas Station 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/gas-station/#/descr ...

  8. LeetCode: Unique Paths II 解题报告

    Unique Paths II Total Accepted: 31019 Total Submissions: 110866My Submissions Question Solution  Fol ...

  9. Leetcode 115 Distinct Subsequences 解题报告

    Distinct Subsequences Total Accepted: 38466 Total Submissions: 143567My Submissions Question Solutio ...

随机推荐

  1. MapReduce08 数据清洗(ETL)和压缩

    目录 数据清洗(ETL) ETL清洗案例 需求 需求分析 实现代码 编写WebLogMapper类 编写WebLogDriver类 打包到集群运行 压缩 概念 MR支持的压缩编码 压缩算法对比 压缩性 ...

  2. Hive(十一)【压缩、存储】

    目录 一.Hadoop的压缩配置 1.MR支持的压缩编码 2.压缩参数配置 3.开启Mapper输出阶段压缩 4.开启Reduceer输出阶段 二.文件存储 1.列式存储和行式存储 2.TextFil ...

  3. Apache2配置文件解读

    每次碰到都不知道具体的作用,所以来分析一下 配置文件结构 apache2在启动的时候自动读取/etc/apache2/apache2.conf文件的配置信息,不同的配置项按功能分布在不同的文件中,然后 ...

  4. 100个Shell脚本——【脚本4】自定义rm命令

    [脚本4]自定义rm命令 linux系统的rm命令太危险,一不小心就会删除掉系统文件. 写一个shell脚本来替换系统的rm命令,要求当删除一个文件或者目录时,都要做一个备份,然后再删除.下面分两种情 ...

  5. Oracle中建表及表操作

    一.创建表 Oracle中的建表语句:create table 表名( 字段名1 数据类型 列属性,字段名2 数据类型 列属性,...... ) 如:创建表OA_DM.DM_GY_USER https ...

  6. Can we use function on left side of an expression in C and C++?

    In C, it might not be possible to have function names on left side of an expression, but it's possib ...

  7. PhoneGap打包webApp

    因为我只弄了Andriod的环境,所以在此只以Andriod为例. 使用PhoneGap搭建Android开发的项目整体步骤如下: 安装java环境. 安装ant构建工具. 安装android的开发环 ...

  8. window 查看端口占用情况

    查看哪个进程在用 netstat -aon|findstr "8080" TCP    0.0.0.0:8080           0.0.0.0:0              ...

  9. RocketMQ应用及原理剖析

    主流消息队列选型对比分析 基础项对比 可用性.可靠性对比 功能性对比 对比分析 Kafka:系统间的流数据通道 RocketMQ:高性能的可靠消息传输 RabbitMQ:可靠消息传输 RocketMQ ...

  10. 【Matlab】取整函数:fix/round/floor/ceil

    fix-向零方向取整.(向中间取整) round-向最近的方向取整.(四舍五入) floor-向负无穷大方向取整.(向下取整) ceil-向正无穷大方向取整.(向上取整)