石子游戏

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

定义二维数组 dp[i][j],其行数和列数都等于石子的堆数,dp[i][j]表示当剩下的石子堆为下标 i 到下标 j 时,当前玩家与另一个玩家的石子数量之差的最大值。

则有转移方程:

注意是轮流取,所以是减。

dp[i][j]=max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1])

class Solution {public:bool stoneGame(vector<int>& piles) {int s = piles.size();vector<  vector < int > > dp(s, vector<int >(s, 0));for (int i = 0; i < s; i++){dp[i][i] = piles[i];}for (int i = 0; i<s; i++){//只有i<j才有意义for (int j = i+1; j < s; j++){dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);}}return dp[0][s - 1];}
};

石子游戏 II

动态规划

用动态规划 ,首先我们要存储状态,那么肯定需要存储的状态即为当前的下标,和M的值。

如果只有一堆的话,自然是很好计算的,全部取完即可。但是剩很多堆时不好算(依赖后面的状态)。所以我们选择从后往前递推。

那么定义dp[i][j]表示,剩余i堆时,M=j时,先取的人能获得的最多石子数。

自然有 i+2*M>=len,可以取完剩下的,那就取完。

剩下的堆数不能全部取走,那么最优情况就是让下一个人取的更少。

dp[i][M] = max(dp[i][M], presum - dp[i + x][max(x, M)])

 class Solution {public:int stoneGameII(vector<int>& piles) {int n = piles.size();vector<vector<int>>dp(n, vector<int>(n + 1, 0));int presum;for (int i = n - 1; i >= 0; i--){presum += piles[i];for (int M = 1; M <= n; M++){if (i + 2 * M >= n)dp[i][M] = presum;else {for (int x = 1; x <= 2 * M; x++){dp[i][M] = max(dp[i][M], presum - dp[i + x][max(x, M)]);}}}}return dp[0][1];}};

递归

 class Solution {private:unordered_map<pair< int, int>, int > m;vector<int> s;//后缀和int n;int dfs(int i, int M){//从i开始取if (m.find({ i,M }) != m.end())return m[{i, M}];if (i >= n)return 0;if (i + M * 2 >= n)return s[i];int best = 0;for (int x = 1; x <=2 * M; x++){//减去对方最优策略best = max(best, s[i] - dfs(i+x, max(x, M)));}m[{i, M}] = best;return best;}public:int stoneGameII(vector<int>& piles) {this->n = piles.size();s = vector<int>(n+1, 0);for (int i = n - 1; i >= 0; i--)s[i] = s[i + 1] + piles[i];return dfs(0, 1);}};

石子游戏III

定义dp[i]为剩余i个时,先手获得的最大分数。那么留给下一位的最大分数只能为dp[i+1],dp[i+2],dp[i+3]中的一个。

presum为i到数组末尾的和。

dp[i]=max(presum[i]-dp[j]), i+1<=j<=i+3

所以 dp[i]=presum[i]-min(dp[j])

 class Solution {public:string stoneGameIII(vector<int>& stoneValue) {int n = stoneValue.size();vector<int> presum(n, 0);presum[n - 1] = stoneValue[n - 1];for (int i = n - 2; i >= 0; i--){presum[i] = presum[i + 1] + stoneValue[i];}vector<int> dp(n+1, 0);dp[n] = 0;for (int i = n - 1; i >= 0; i--){int best = dp[i+1];for (int j = i + 2; j <= i + 3 && j <=n; j++)best = min(best, dp[j]);dp[i] = presum[i] - best;}if (dp[0] * 2 == presum[0])return "Tie";return dp[0]*2>presum[0]? "Alice" : "Bob";}};

石子游戏IV

假设dp[i]时先手处于必胜态,则一定有dp[i-k*k]为必败态。也就是说,当先手在面对 i 颗石子时,可以选择取走 k * k颗,剩余的 i-k * k 颗对于后手来说是必败态,因此先手会获胜。

边界条件为 f[0]=false,即没有石子时,先手会输掉游戏。

动态规划

 class Solution {public:bool winnerSquareGame(int n) {vector<int>dp(n + 1, 0);for (int i = 0; i <= n; i++){//从剩余0个开始递推for (int k = 1; k * k <= i; k++){if (!dp[i-k*k]){dp[i] = 1;}}}return dp[n];}};

DFS

class Solution {private:unordered_map<int, int> cache;bool dfs(int n)//剩余n个时{if (n == 1) return true;if (cache.find(n) != cache.end())return cache[n];for (int i = 1; i * i <= n; i++){if (!dfs(n - i * i))//另一个人剩余n-i*i必输时,n就赢{cache[n] = 1;return true;}}cache[n] = 0;return false;}public:bool winnerSquareGame(int n) {return dfs(n);}};

石子游戏V

用dp[i][j]表示当 Alice 面对数组 stoneValue中从位置 i 到 j 这一段连续的石子时,她能获得的最大分数。由于 Alice 需要选择将这一段石子分成两部分,因此我们可以枚举分隔位置 。

对于左右两边的和,记为suml,sumr

suml>sumj,那么肯定丢弃左侧部分,对应的,小于则丢弃右边部分。

如果两行的值相等,Bob 让 Alice 决定丢弃哪一行。那就两边继续搜索比谁大。

 class Solution {private:vector<vector<int>> cache;int dfs(vector<int>& sum, int l, int r){if(l == r)return 0;if (cache[l][r]!=-1)return cache[l][r];cache[l][r] = 0;for (int i = l + 1; i <= r; i++){int lsum = sum[i] - sum[l];int rsum = sum[r+1] - sum[i];if (lsum < rsum)cache[l][r] = max(cache[l][r], lsum + dfs(sum, l, i - 1));else if (lsum > rsum)cache[l][r] = max(cache[l][r], rsum + dfs(sum, i, r));elsecache[l][r] = max(cache[l][r],lsum+max(dfs(sum, l, i - 1), dfs(sum, i, r)));}return cache[l][r];}public:int stoneGameV(vector<int>& stoneValue) {int size = stoneValue.size();cache = vector<vector<int>>(size + 1, vector<int>(size + 1, -1));vector<int> sum(size + 1, 0);//[n,m)for (int i = 0; i < size; i++)sum[i + 1] = sum[i] + stoneValue[i];return dfs(sum, 0, size - 1);}};

石子游戏 VI

我们可以这么想:石子分数高我们需要拿,有石子对于bob来说分数也很高,我们也需要拿(因为尽可能要让bob的分数最少)。

存在两个方案 :

alice的 a1,b1

bob的 a2,b2

我们比较a1-b2(alice拿了a1,bob只能拿b2)与b1-a2的差值,即比较a1+a2和b1+b2。

由此 将两数组合并,排序,偶数位 alice的 ,获得排序后对应下标的石头价值即可。

 class Solution {public:int stoneGameVI(vector<int>& aliceValues, vector<int>& bobValues) {int size = aliceValues.size();int sum_a = 0, sum_b = 0;vector<pair<int, int>> s(size, {0,0});for (int i = 0; i < size; i++){s[i] = { aliceValues[i] + bobValues[i],i };}sort(s.begin(), s.end(), [](const pair<int, int> a, const pair<int, int> b){return a.first > b.first;});for (int i = 0; i < size; i++){if (i % 2)sum_b += bobValues[s[i].second];elsesum_a += aliceValues[s[i].second];}if (sum_a == sum_b)return 0;return sum_a > sum_b? 1:-1;}};

石子游戏 VII

所以 最大得分差 可以理解为此次操作之后,A 所收获的价值 - 下次B 比A的得分差的最大值。
如果是 B 操作,那么就是 B 所收获的价值 - 下次A比B得分差的最大值
所以这次 DP 也用类似的状态。维护一个sum前缀和数组。

dp[i][j]为i-j的当前玩家与另一个玩家得分差的最大值。

当j-i==1,肯定删掉一个较小的石头,取最大得分。

剩下的情况 要么左边删,要么右边删。

class Solution {public:int stoneGameVII(vector<int>& stones) {int size = stones.size();vector<int> sum(size + 1, 0);for (int i = 0; i < size; i++){sum[i + 1] = sum[i] + stones[i];//[0,i);}vector<vector<int>>dp(size, vector<int>(size, 0));for (int i = size - 1; i >= 0; i--){for (int j = i + 1; j < size; j++){if (j - i == 1)dp[i][j] = max(stones[i], stones[j]);else//[0,j+1)-[0,i+1)=[i+1,j]dp[i][j] = max(sum[j + 1] - sum[i + 1] - dp[i + 1][j],//[0,j)-[0,i)    =[i,j-1]sum[j] - sum[i] - dp[i][j - 1]);}}return dp[0][size - 1];}};

leetcode——石子游戏系列题目相关推荐

  1. leetcode 买卖股票系列题目总结

    总结:买卖股票系列题目 1.买卖股票的最佳时机(简单) 121. 买卖股票的最佳时机 难度简单1093 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易( ...

  2. 取石子游戏系列(1)

    题目来自<编程之美>一排石头的游戏 N块石头排成一列,每块石头都有自己的固定位置,也就是相当于有自己的编号一样.两个玩家依次取石头,每个玩家每次可以取其中任意一块石头,或者相邻的两块,最后 ...

  3. LeetCode——字节跳动系列题目

    转至:https://blog.csdn.net/Uupton/article/details/84640146 今天登陆leetcode发现探索区多了字节跳动的专栏,特意用了一下午去刷,有些是之前刷 ...

  4. leetcode 跳跃游戏系列 c++

    文章目录 [55. 跳跃游戏](https://leetcode-cn.com/problems/jump-game/) [45. 跳跃游戏 II](https://leetcode-cn.com/p ...

  5. LeetCode 1195. Fizz Buzz Multithreaded--并发系列题目--Java 解法--AtomicInteger/CountDownLatch/CyclicBarrier

    题目地址:Fizz Buzz Multithreaded - LeetCode Write a program that outputs the string representation of nu ...

  6. 动态规划---石子游戏

    动态规划---石子游戏 石子游戏(leetcode877) 石子游戏(leetcode1140) 石子游戏(leetcode1686) 石子游戏(leetcode877) 题目描述 亚历克斯和李用几堆 ...

  7. 信息学奥赛一本通 1218:取石子游戏 | OpenJudge NOI 2.5 6266:取石子游戏

    [题目链接] ybt 1218:取石子游戏 OpenJudge NOI 2.5 6266:取石子游戏 [题目考点] 1. 博弈:完全信息博弈 博弈树: 博弈树的结点对应于某一个棋局,其分支表示走一步棋 ...

  8. LeetCode 1690. 石子游戏 VII(博弈DP)

    文章目录 1. 题目 2. 解题 1. 题目 石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 . 有 n 块石子排成一排.每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获 ...

  9. LeetCode 1686. 石子游戏 VI(贪心)

    文章目录 1. 题目 2. 解题 283 / 1660,前17% 681 / 6572,前10.4% 1. 题目 Alice 和 Bob 轮流玩一个游戏,Alice 先手. 一堆石子里总共有 n 个石 ...

最新文章

  1. bootstrap上传文件美化
  2. 识骨寻踪:少年,我看你骨骼清奇,不如来看看这本书。
  3. ise 时钟约束_「新手入门」ISE工程升级到Vivado及板级信号调试技术
  4. Python time asctime()方法
  5. Centos7: 配置IO调度
  6. 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)
  7. PDE2 three fundamental examples
  8. Apple 隐私政策
  9. paip.提升用户体验----表格显示及控件布局错乱的问题
  10. MFC使用ADO对象开发数据库应用程序
  11. 初入门-游戏设计思路拆解
  12. 如何用PS的量度标尺工具调整图片
  13. 《游戏设计理论》参考版
  14. Redis与数据库的数据一致性
  15. 统计与分布之伯努利分布与二项分布
  16. 【调剂】关于开通上海第二工业大学2022年硕士研究生招生预调剂系统的通知
  17. 考研面试php,考研复试 | 盘点:这些院校已公布2019考研复试内容
  18. android地图定位到海洋,如何利用卫星导航技术进行高精度海洋测绘定位
  19. 购买运动耳机应该考虑什么问题,六款运动耳机推荐
  20. html网页轮播速度,纯HTML+JS实现轮播

热门文章

  1. C++用桌面图标系列之三【整理-时钟】
  2. poj 1187 陨石的秘密
  3. 淘宝店铺装修(首页和宝贝详情页显示不一致)
  4. python3 scrapy抓取今日头条视频(西瓜视频)
  5. 随机权重的PSO算法
  6. 如何修改ICO文件的尺寸
  7. 3D Question Answering
  8. 编写一个python程序判断用户输入的8位银行卡_用Python编写的程序,提示用户输入一个由7位数字组成的帐号?...
  9. 历数国内外知名制作人~~制作人发展趋势
  10. 计算机转换几种,文件转换 篇一:有多少种文件格式转换的方法,你造吗?