1. 题目来源
链接:877. 石子游戏
2. 题目解析
本题可以偷鸡。
博弈论:
-
由于序列是偶数长度,且只能从两端开始选。假设下标从 1 开始,那么先手玩家一定能够在两端选择 一奇一偶 的下标,有两种情况。而后手玩家就只能在两端选择 同奇同偶 的下标。
-
所以,先手玩家在第一次操作时就能够计算 奇数下标和偶数下标 的和哪个大,每一次决策都限制后手玩家只能选择最优奇偶性序列的对立面即可。
区间 dp
:
f[l, r]
表示在区间[l, r]
之间先手玩家在最坏情况下的最大得分和后手玩家最大得分的差值。- 如果
f[l, r] > 0
说明先手胜利,=0
则为平局,<0
则为先手失败 - 状态转移:当前玩家
f[i][j]
现在只能在i
位置选,j
位置选。- 选择
i
位置的话,剩余的区间是[i+1, j]
,考虑f[i+1][j]
这个状态,是选完i
之后,此时的后手玩家作为先手玩家与目前的先手玩家的最大得分差值(有点扰,多读几遍),假设为B-A
,是针对后手玩家来讲最好的情况。那么先手玩家目前最坏的得分差值其实就是A-B
,相差一个负号,即先手玩家选完i
位置的数后,最坏的得分差值就是w[i]+(A-B) = w[i]-f[i+1][j]
- 注意这是区间
dp
,短区间的状态已经被计算出来了。 且长区间需要短区间的状态,也是区间 dp 的经典应用条件。 - 同理,选择
j
的话,转移应该是w[j]-f[i][j-1]
。
- 选择
- 最后只需要判断
f[0][n - 1]>0
即可知道是否先手必胜。
针对博弈论问题,若有必胜、必败存在,一定是在最坏情况下能必胜,那就一定能必胜。即只需要保证最坏情况下最好就能判断胜负状态。
时间复杂度: O ( n 2 ) O(n^2) O(n2) 、 O ( 1 ) O(1) O(1)
空间复杂度: O ( 1 ) O(1) O(1)
区间 dp O ( n 2 ) O(n^2) O(n2)
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int n = piles.size();
vector<vector<int>> f(n, vector<int>(n));
for (int len = 1; len <= n; len ++ )
for (int i = 0; i + len - 1 < n; i ++ ) {
int j = i + len - 1;
if (len == 1) f[i][j] = piles[i];
else f[i][j] = max(piles[i] - f[i + 1][j], piles[j] - f[i][j - 1]);
}
return f[0][n - 1] > 0;
}
};
O ( 1 ) O(1) O(1)
class Solution {
public:
bool stoneGame(vector<int>& piles) {
return true;
}
};