Daliy Algorithm (dp,博弈)-- day 75
Nothing to fear
种一棵树最好的时间是十年前,其次是现在!
那些你早出晚归付出的刻苦努力,你不想训练,当你觉的太累了但还是要咬牙坚持的时候,那就是在追逐梦想,不要在意终点有什么,要享受路途的过程,或许你不能成就梦想,但一定会有更伟大的事情随之而来。 mamba out~
2020.5.8
人一我十, 人十我百,追逐青春的梦想,怀着自信的心,永不言弃!
补更5.8
lc1277. Count Square Submatrices with All Ones
从出状态到末状态如果比较困难,可以尝试从后一状态考虑都可以由哪些状态共同决策而来,
关键还是在于状态得表示
f[i][j] = x 表示以i , j为正方形右下角得元素得最大边长为 x
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int n = matrix.size();
int m = matrix[0].size();
int ans = 0;
int f[n][m];
for(int i = 0;i < n ;i ++)
{
for(int j = 0;j < m;j ++)
{
if(i == 0 || j == 0)f[i][j] = matrix[i][j];
else if(matrix[i][j] == 0)f[i][j] = 0;
else{
f[i][j] = min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1]) + 1;
}
ans += f[i][j];
}
}
return ans;
}
};
lc 877 石头游戏
博弈论dp问题的分析模型
对于本题做一个拓展,原本的题目当中限制了先手必定是其中的某一方且限制了石头的堆数还有。但是我们再这里去掉这个限制,将其堪为一个更普适的问题。即任意一方先出手谁会赢,(但是取法和之前一致)
对于这类问题,根据已有的模型即已经存在的状态表示的策略。
有如下定义:
状态表示:
dp[i][j][fir/sec]
分为两种情况:
dp[i][j][fir] : 对于 i - j 这堆石头中 先手 能获得的最高分
dp[i][j][sec] : 对于 i - j 这堆石头中 后手 能获得的最高分
举例理解一下,假设 piles = [3, 9, 1, 2],索引从 0 开始
dp[0][1][fir] = 9 意味着:面对石头堆 [3, 9],先手最终能够获得 9 分。
dp[1][3][sec] = 2 意味着:面对石头堆 [9, 1, 2],后手最终能够获得 2 分。
状态计算:
对于每一个问题,都有两种状态:
- 选择左边第一堆石头
- 或者选择右边最后一堆
即dp[i][j][who] = max(left选择,right选择)
根据以上总结出状态转移方程:
dp[i][j][fir] = max(plies[i] + dp[i+1][j][sec],plies[j] + dp[i][j-1]);
解释:我作为先手,面对 piles[i...j] 时,有两种选择:
要么我选择最左边的那一堆石头,然后面对 piles[i+1...j]
但是此时轮到对方,相当于我变成了后手;
要么我选择最右边的那一堆石头,然后面对 piles[i...j-1]
但是此时轮到对方,相当于我变成了后手。
if 先手选择左边:
dp[i][j][sec] = dp[i+1][j][fir]
if 先手选择右边:
dp[i][j][sec] = dp[i][j+1][fir]
解释:我作为后手,要等先手先选择,有两种情况:
如果先手选择了最左边那堆,给我剩下了 piles[i+1...j]
此时轮到我,我变成了先手;
如果先手选择了最右边那堆,给我剩下了 piles[i...j-1]
此时轮到我,我变成了先手。
初始时我们需要初始化状态:
dp[i][j][fir] = plies[i]
dp[i][j][sec] = 0
条件: i == j
即面前只有一堆石头:
先手拿完后手就没了
typedef pair<int,int> PII;
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int k = piles.size();
vector<vector<PII>> dp(k,vector<PII>(k,PII(0,0)));
for(int i = 0;i < k ;i ++)
{
dp[i][i].first = piles[i];
dp[i][i].second = 0;
}
for(int i = k - 2; i>= 0; i--)
{
for(int j = i + 1;j < k;j ++)
{
int left = piles[i] + dp[i+1][j].second;
int right = piles[j] + dp[i][j-1].second;
// 如果先手选了左边
if(left >= right)
{
dp[i][j].first = left;
dp[i][j].second = dp[i+1][j].first;
}else{
dp[i][j].first = right;
dp[i][j].second = dp[i][j-1].first;
}
}
}
return dp[0][k-1].first > dp[0][k-1].second;
}
};