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]是最大值,即题干要求每个人的状态都是最佳。
我们的每次先手状态,都是以我们取完一堆石子后,对方以我们取完一堆石子的状态再”先手“的基础上建立的。

浙公网安备 33010602011771号