dp练习第六天-Leetcode877. 石子游戏

题目

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

 

示例:

输入:[5,3,4,5]
输出:true
解释:
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。
 

提示:

2 <= piles.length <= 500
piles.length 是偶数。
1 <= piles[i] <= 500
sum(piles) 是奇数。


分析

拿到一个博弈问题时,最先想到使用dp求解。博弈问题大多数建立的是一个二维dp数组,dp[i][j]中存储的是从i到j的博弈问题最优解。

分析题干,如果想要获胜,需要分数更高,所以dp中存储的应该是两人得分之差,而不是某个人的分数。

分析状态转移方程:当i>j时,没有实际意义。当i=j时,因为我们先手,所以存储的那个数字就是石子在i的位置的数量。当i<j时,我们是先手,但是只能选择第i堆或者第j堆石子,需要找出两种情况的最大值。

如果取第i堆石子,那么我们的dp[i][j]=piles[i]-dp[i+1][j]; 如果取第j堆石子,那么我们的dp[i][j]=piles[j]-dp[i][j-1];

最后我们考虑的是循环的顺序,首先,dp矩阵的斜对角线已知,其次,由状态转移方程我们知道,要想求dp[i][j],必须知道dp[i][j-1]和dp[i+1][j],所以我们对于i,要从大到小依次递减,而对于j,我们要从小到大依次递增。


代码

 1 class Solution {
 2 public:
 3     int dp[500][500];
 4     bool stoneGame(vector<int>& piles) {
 5         for(int i=0;i<piles.size();i++)
 6         {
 7             dp[i][i]=piles[i];//写对角线已知
 8         }
 9         for(int i=piles.size();i>=0;i--)//i递减
10         {
11             for(int j=i+1;j<piles.size();j++)//j递增
12             {
13                 dp[i][j]=max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
14             }
15         }
16         if(dp[0][piles.size()-1]>0)//如果从头到尾博弈的dp值大于0,那么获胜
17         {
18             return true;
19         }
20         else
21         {
22             return false;
23         }
24     }
25 };

回顾

本题在分析的时候可能出现的错误思维(也是我在做题过程中出现的差错):为什么dp[i][j-1]是我先手,dp[i][j]还是我先手?

原因是,因为每个人都是最佳状态,都想获胜,所以在dp[i][j]先手的人是“我”,那么对应dp[i][j-1]先手的人则不是“我”,但是分析的时候要将dp[i][j-1]以我的视角分析,这样才能确保dp[i][j-1]是最大值,即题干要求每个人的状态都是最佳。

我们的每次先手状态,都是以我们取完一堆石子后,对方以我们取完一堆石子的状态再”先手“的基础上建立的。

posted @ 2021-02-17 16:33  _厨师沙拉  阅读(89)  评论(0)    收藏  举报