LeetCode:877. Stone Game 486. Predict the Winner
这一题无论是骚方法还是就普通的dp方法都给我很大启示。
解法来源:https://leetcode.com/problems/stone-game/discuss/154610/DP-or-Just-return-true
1.骚方法
直接return true。因为第一个拿的肯定会赢。
原因是总共有偶数堆,那么第一个拿的可以永远拿编号为偶数的堆或者永远拿编号为奇数的堆。那么知道了是偶数的堆多还是奇数的堆多,拿它们就行了。
2.dp方法
上面的骚方法是由缺点的,它是能够保证肯定能赢的,但不是最优的,我们不知道最多能够赢多少。
这里使用dp方法来计算。
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int sz = piles.size();
vector<vector<int>> dp(sz, vector<int>(sz, 0));
for (int i = 0; i < sz; ++i)
dp[i][i] = piles[i];
for (int i = sz-1; i >= 0; --i)
for (int j = i+1; j < sz; ++j)
dp[i][j] = max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1]);
return dp[0][sz-1] > 0;
}
};
//dp[i][j]:先从i-j中挑的人能赢过对手的最大数目。注意是先挑的人
//如果挑i,则为pile[i] - dp[i+1][j]。注意dp[i+1][j]就是对手先挑了,所以这么写是对的。
//具体可以解释为(比如Lee先挑了):lee挑的i堆 - (i+1到j中Alex挑的 - i+1到j中Lee挑的) = lee挑的i堆 + i+1到j中Lee挑的 - i+1到j中Alex挑的
//如果挑j,则为pile[j] - dp[i][j-1]
//所以dp[i][j] = max(pile[i] - dp[i+1][j], pile[j] - dp[i][j-1])
解释都在注释里面了。
注意这里设状态的方法,没想过还能够这么做。
3.一维的dp
class Solution {
public:
bool stoneGame(vector<int>& piles) {
vector<int> dp = piles;
for (int d = 1; d < piles.size(); ++d)
for (int i = 0; i < piles.size() - d; ++i)
dp[i] = max(piles[i] - dp[i+1], piles[i+d] - dp[i]);
return dp[0] > 0;
}
};
//dp[i]:从i-i+d中先挑的人的赢过对手的最大数目
同样注意这里的状态和转移方程。每次都使用了该位置上次存储的状态。
另外,做这题可以使用top-down + memo的方法。
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int sz = piles.size();
map<pair<int, int>, int> m;//使用二维表也是可以的
return helper(m, 0, sz-1, piles) > 0;
}
private://把辅助函数当作map的get来用。有就返回,没有就计算出来。或者当作二维表的读取函数来做。
int helper(map<pair<int, int>, int>& m, int i, int j, vector<int>& piles) {
if (i == j)
return piles[i];
auto key = make_pair(i, j);
if (m.find(key) != m.end())
return m[key];
m[key] = max(piles[i] - helper(m, i+1, j, piles), piles[j] - helper(m, i, j-1, piles));
return m[key];
}
int getHash(int i, int j) {
return stoi(to_string(i) + to_string(j));
}
};
想要知道更多的top-down + memo,看:https://blog.csdn.net/weixin_43462819/article/details/100667574
486和这一题类似:
class Solution {
public:
bool PredictTheWinner(vector<int>& nums) {
int sz = nums.size();
if (sz == 1)
return true;
vector<vector<int>> dp(sz, vector<int>(sz, 0));
for (int i = sz-1; i >= 0; --i)
for (int j = i; j < sz; ++j) {
if (i == j)
dp[i][j] = nums[i];
else
dp[i][j] = max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1]);
}
return dp[0][sz-1] >= 0;
}
};
这里注意的是两个人玩游戏这种状态怎么设:这里是i->j,先玩的人赢对手多少,这样状态之间才可以有关系,才能够写出状态方程。
如果直接写i->j,先玩的人能不能赢,这样转移方程是写不出来的。