作者: 负雪明烛
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. MySQL_集群

    管理节点:192.168.31.66 sql节点1+data1节点:192.168.31.42 sql节点2+data2节点:192.168.31.128 llll

  2. 40-3Sum Closest

    3Sum Closest My Submissions QuestionEditorial Solution Total Accepted: 76185 Total Submissions: 2621 ...

  3. Visual Studio Code常用操作整理

    Live Server插件可以在保存html文件后实时地刷新页面 在html文件中键入"! +Tap"会生成一个html模板 保存文件:Ctrl+S 文件跳转:Ctrl+P 文件内 ...

  4. dart系列之:HTML的专属领域,除了javascript之外,dart也可以

    目录 简介 DOM操作 CSS操作 处理事件 总结 简介 虽然dart可以同时用作客户端和服务器端,但是基本上dart还是用做flutter开发的基本语言而使用的.除了andorid和ios之外,we ...

  5. Spark中的分区方法详解

    转自:https://blog.csdn.net/dmy1115143060/article/details/82620715 一.Spark数据分区方式简要 在Spark中,RDD(Resilien ...

  6. 复制virtualbox虚拟硬盘

    D:\VirtualBox\VBoxManage.exe clonevdi F:\virtualbox\rac1\rac1.vdi F:\virtualbox\rac2\rac2.vdi 虚拟机软件安 ...

  7. go 代理

    环境变量中设置 #GO111MODULE=auto GOPROXY=https://goproxy.io 如果不第一次,则在命令行设置 go env -w GO111MODULE=on go env ...

  8. Linux系统信息查看命令(ZZ)

    http://hi.baidu.com/thinkdifferent/blog/item/22f4a80161630e011d958384.html转自一个baidu师兄的博客,很好的一个总结,推荐下 ...

  9. 【力扣】19. 删除链表的倒数第 N 个结点

    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点. 进阶:你能尝试使用一趟扫描实现吗? 示例 1: 输入:head = [1,2,3,4,5], n = 2输出:[1,2,3,5]示例 ...

  10. 【Spark】【RDD】初次学习RDD 笔记 汇总

    RDD Author:萌狼蓝天 [哔哩哔哩]萌狼蓝天 [博客]https://mllt.cc [博客园]萌狼蓝天 - 博客园 [微信公众号]mllt9920 [学习交流QQ群]238948804 目录 ...