877. 石子游戏

news/2023/6/9 20:30:32

877. 石子游戏

    • 题目
    • 算法设计:奇偶
    • 算法设计:动态规划

 


题目


 


算法设计:奇偶

最简单的情况,只有2堆石子(石子奇数),先稳赢。

但是四堆情况不同了,如 [3 7 2 3]。

不能直接选最大的,只能选数组开头和末尾。

那我们就按照取法的顺序,把 3 7 2 3 分成 2 组,偶数组 7 3,和奇数组 3 2。

  • 如 3 7 2 3,先手要赢,必须取到 7,怎么可以取到 7 呢?
  • 7 是奇数,就取奇数组,7 是偶数,就取偶数组,因为奇数、偶数必然有一个更大,先手必然赢。
  • 先手取第一个(奇数),后手只能取第二、四个(偶数),继续维持奇偶性质即可
  • 先手取第四个(偶数),后手只能取第一、三个(奇数),继续维持奇偶性质即可

因为石子是奇数,堆数是偶数,取法只能是数组开头和结尾,你的取法要么是奇数组,要么是偶数组。

那么只需要计算出,哪个组大,先手就取哪个组即可。

class Solution {
public:bool stoneGame(vector<int>& piles) {return true;       // 先手必赢}
};

 


算法设计:动态规划

官方的题解的状态是这样定义的: dp(i, j) 为先手可以获得的最大分数,初看这个状态很正确,实际是一个非常明显的错误.

因为当我们考虑 dp(i, j) 由 dp(i + 1, j) 转移过来时,即取了头部下标为i的这个数, 然后我们来看 dp(i + 1, j) 这个状态,按照官方的定义 dp(i + 1, j) 这个状态为 Alice 可以获得的最大分数,这里显然是错误的,因为 dp(i + 1, j) 这个状态不是 Alice。

那么正确的状态定义应该是啥?

  • 答案是从区间 [L, R] 这个状态,先手和后手最大石子差

这个状态是不是看起来很简单,而且可能会有很多人疑问,这个状态的 id 怎么没有记录,是 A,还是 B 到达了这个状态呢?

其实这就是这类问题的关键:因为是两个人在博弈,所以从当前状态转移到下一个状态时,就体现了 id 的变化,比如说当前状态是A,因为是两个人在玩,下一个状态就是B,这里很关键。

为什么状态定义为二维 dp[i][j],因为这题和子序列问题一样,是不连续的序列,通常需要俩个指针来定位,如下表:

ii+1j-1j
53461657

分析,最大石子差 f[i][j] 从哪里来?

  • 从左端拿,先手拿 piles[i],后手从 f[i+1][j] 的俩端中选出最大值,双方石子差为 piles[i] - f[i+1][j],结果为正,说明先手赢

  • 从右端拿,先手拿 piles[j],后手从 f[i][j-1] 的俩端中选出最大值,双方石子差为 piles[j] - f[i][j-1],结果为负,说明后手赢

  • 状态转移方程:f[i][j]=max(piles[i]−f[i+1][j],piles[j]−f[i][j−1])f[i][j] = max(piles[i]-f[i+1][j], ~~piles[j] - f[i][j-1])f[i][j]=max(piles[i]f[i+1][j],  piles[j]f[i][j1])

代码实现:

  • 状态转移方向:想计算出 f[i][j] 就需要知道 f[i][j-1]、f[i+1][j]。
  • 最简单情况:那 f[i][j-1]、f[i+1][j] 怎么计算出来?最简单的情况是,下图的初始化。
  • 循环遍历方向:为了保证计算 f[i][j] 时,f[i][j-1]、f[i+1][j] 已经计算出来,循环遍历为:

斜着遍历:

或者,反着遍历:

class Solution {
public:bool stoneGame(vector<int>& piles) {int n = piles.size();vector<vector<int>> f(n, vector<int>(n));for (int i = 0; i < n; i++)               // 最简单的情况f[i][i] = piles[i];for (int i = n - 1; i >= 0; i--)          // 反着遍历for (int j = i + 1; j < n; j++) {int a = piles[i] - f[i + 1][j];int b = piles[j] - f[i][j - 1];f[i][j] = max(piles[i] - f[i + 1][j], piles[j] - f[i][j - 1]);}return f[0][n - 1] > 0;                   // 正数,先手赢}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.exyb.cn/news/show-4559410.html

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

c++计算键入参数 输出圆柱体积 以及表面积 类的使用

制作一个密闭圆柱形的油桶装汽油&#xff0c;铁皮每平米售价为25元。设计一个Jerrican类&#xff0c;可以求出圆柱形油桶的表面积和容积&#xff08;大约&#xff09;&#xff0c;同时计算制作一个这样的油桶的材料成本是多少&#xff1f; //相关代码// jisuantiji.cpp: 主项目…

考虑体积重量的装箱问题(贪婪策略装箱)—— 基于遗传算法

考虑体积重量的装箱问题&#xff08;贪婪策略装箱&#xff09;—— 基于遗传算法 1 装箱问题简介 经典装箱问题要求把一定数量的物品放入容量相同的箱子中&#xff0c;在满足每个箱子装载物品的大小之和不超过箱子容量的约束下&#xff0c;最小化箱子数目。装箱问题是复杂的离…

考虑体积重量的装箱问题(箱子装载平衡)— 基于遗传算法

考虑体积重量的装箱问题&#xff08;箱子装载平衡&#xff09;— 基于遗传算法 1 前言 经典装箱问题要求把一定数量的物品放入容量相同的箱子中&#xff0c;在满足每个箱子装载物品的大小之和不超过箱子容量的约束下&#xff0c;最小化箱子数目。在上一篇装箱问题的博文【考虑…

考虑体积重量的01背包问题—基于遗传算法

考虑体积重量的01背包问题—基于遗传算法 1 背包问题简介 经典背包问题可以描述为&#xff1a;给定一组物品&#xff0c;每种物品都有自己的重量/体积和价值&#xff0c;要求把一定数量的物品放入背包&#xff0c;在满足背包载重/容积的约束下&#xff0c;最大化背包内的物品…

ESPNet: 自动驾驶领域轻量级分割模型

论文标题&#xff1a;ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation 论文地址&#xff1a;https://arxiv.org/pdf/1803.06815v2.pdf 开源地址&#xff1a; https://github.com/sacmehta/ESPNet 论文思想 ESPNet是用于语义分割的轻量…

音乐疗法可缓解精神分裂症状

来源&#xff1a;中国数字科技馆 据报道&#xff0c;伦敦大学帝国理工学院的研究人员在4所医院里进行了一项小规模研究&#xff0c;将参与者分成两组&#xff0c;一组进行常规治疗&#xff0c;另一组接受音乐治疗(8&#xff5e;12个音乐治疗疗程)&#xff0c;患者被鼓励通过一…

mysql数据库主从同步_详解MySQL数据库设置主从同步的方法

简介MySQL主从同步是目前使用比较广泛的数据库架构&#xff0c;技术比较成熟&#xff0c;配置也不复杂&#xff0c;特别是对于负载比较大的网站&#xff0c;主从同步能够有效缓解数据库读写的压力。MySQL主从同步的机制&#xff1a;MySQL同步的流程大致如下:1、主服务器(master…

Spark常见报错与问题解决方法

查看Spark日志与排查报错问题的方法请看&#xff1a;https://blog.csdn.net/qq_33588730/article/details/109353336 1. org.apache.spark.SparkException: Kryo serialization failed: Buffer overflow 原因&#xff1a;kryo序列化缓存空间不足。 解决方法&#xff1a;增加…