目录
基本动态规划:一维
不能使用递归,会超时。
class Solution {
public:
int fib(int n) {
int mod = 1000000007;
if(n < 2)
{
return n;
}
int first = 0, second =1;
int count;
for(int i = 2;i < n+1;i++)
{
count = (first+second)%mod;
first = second;
second = count;
}
return count;
}
};
跳上N节台阶的可能为N-1 跳一次1 和N-2 跳一次2加起来的可能。
class Solution {
public:
int numWays(int n) {
int mod = 1000000007;
if(n == 0 | n == 1)
return 1;
int first = 1;
int second = 1;
int count;
for(int i = 2; i <= n;i++)
{
count = (first + second)%mod;
first = second;
second = count;
}
return count;
}
};
记录一个历史的最低价格
class Solution {
public:
int maxProfit(vector<int>& prices) {
int inf = 1000000000;
int minprice = inf , maxprice = 0;
for(int pe : prices)
{
maxprice = max(maxprice,pe - minprice);
minprice = min(pe , minprice); //记录一个历史最低价格
}
return maxprice;
}
};
比较当前值 及其 当前值+pre的和,存为当前的pre
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, maxAns = nums[0];
for(int a : nums)
{
pre = max(pre+a , a);
maxAns = max(pre , maxAns);
}
return maxAns;
}
};
定义一个数组 dp,dp[i] 表示抢劫到第 i 个房子时,可以抢劫的最大数量。我们考虑 dp[i],此时可以抢劫的最大数量有两种可能,一种是我们选择不抢劫这个房子,此时累计的金额即为dp[i-1];另一种是我们选择抢劫这个房子,那么此前累计的最大金额只能是 dp[i-2],因为我们不能够抢劫第 i-1 个房子,否则会触发警报机关。因此本题的状态转移方程为 dp[i] = max(dp[i-1], nums[i-1] + dp[i-2])。
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
vector<int> dp(n+1,0); //表示抢到第i个房间之前最大金额
dp[1] = nums[0]; //抢到第一个房子之前,就是nums[0]
for(int i = 2;i <= n ;i++)
{
dp[i] = max(dp[i-1],dp[i-2]+nums[i-1]);
//第i个房子不抢 , 或者 抢 然后加上 当前房子金额nums[i-1]
}
return dp[n];
}
};
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
int cur = 0;
int pre = 0;
int ppre = 0;
for(int i = 0;i < n ;i++)
{
cur = max(pre , ppre+nums[i]);
ppre = pre;
pre = cur;
}
return cur;
}
};
子数组必定满足 num[i] - num[i-1] = num[i-1] - num[i-2]。然而由于我们对于 dp 数组的定义通常为以 i 结尾的,满足某些条件的子数组数量,而等差子数组可以在任意一个位置终结,因此此题在最后需要对 dp 数组求和。
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n = nums.size();
if(n <= 2) return 0;
/// 以i结尾
vector<int> dp(n ,0);
for(int i = 2;i < n ;i++)
{
if(nums[i] - nums[i-1] == nums[i-1] - nums[i-2])
{
dp[i] = dp[i-1]+1;
}
}
return accumulate(dp.begin(),dp.end(),0);
//求出所有位置的和
}
};
基本动态规划:二维
定义一个同样是二维的 dp 数组,其中 dp[i][j] 表示从左上角开始到 (i, j) 位置的最优路径的数字和。因为每次只能向下或者向右移动,我们可以很容易得到状态转移方程 dp[i][j] =min(dp[i-1][j], dp[i][j-1]) + grid[i][j],其中 grid 表示原数组。
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(); // m x n
int n = grid[0].size(); //
vector<vector<int>> dp(m,vector<int>(n,0)); //状态转移方程:dp表示到达(i,j)位置的最短路径
for(int i = 0;i < m;i++)
{
for(int j = 0; j < n; j++)
{
if(i==0 && j==0) dp[i][j] = grid[i][j];
else if(i==0) dp[i][j] = dp[i][j-1] + grid[i][j];
else if(j==0) dp[i][j] = dp[i-1][j] + grid[i][j];
else{
dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
//当前位置的数值加上 到达上或左边的最短路径
}
}
}
return dp[m-1][n-1]; //返回到达右下角的最短路径
}
};
从左上到右下进行一次动态搜索,然后在进行一次从右下到左上的一次动态搜索,来完成四个方向的查找。
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
int m = mat.size();
int n = mat[0].size();
vector<vector<int>> dp(m,vector(n, INT_MAX-1));
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(mat[i][j] == 0)
{
dp[i][j] = 0;
}
else{
if(i > 0)
{
dp[i][j] = min(dp[i][j] , dp[i-1][j]+1);
}
if(j > 0)
{
dp[i][j] = min(dp[i][j] , dp[i][j-1] + 1);
}
//此时dp存储的是INT_MAX-1和上、左+1的最小值;
}
}
}//此时除了【0,0】,应该除了距离就是INT_MAX
for(int i = m-1 ;i >= 0;i--)
{
for(int j = n-1 ;j >= 0;j--)
{
if(mat[i][j] != 0)
{
if( i < m -1)
{
dp[i][j] = min(dp[i][j] ,dp[i + 1][j] + 1);
}
if(j < n - 1)
{
dp[i][j] = min(dp[i][j] ,dp[i][j+1] + 1);
}
} //此时dp存储的是刚刚的dp与下、右+1的最小值。
}
}
return dp;
}
};
//如果使用上题解法,(0,0)为1时会出现问题
方法一:暴力解法
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
if(m == 0 || n == 0)
{
return 0;
}
int maxSide = 0;
for(int i = 0; i < m ;i++)
{
for(int j = 0;j < n;j++)
{
if(matrix[i][j] == '1') //如果遇到一个1作为正方形的左上角
{
maxSide = max(maxSide,1); //存储最大
int currentMaxSide = min(m-i,n-j); //计算当前可能的最大正方形边长
for(int k = 1; k < currentMaxSide ;k++)
{
//判断未来的每一行是否都为1
bool flag = true;
if(matrix[i+k][j+k] == '0') //判断
{
break;
}
for(int m = 0;m < k;m++) //判断新增的行列是否为1
{
if(matrix[i+k][j+m] == '0' || matrix[i+m][j+k] == '0')
{
flag = false;
break;
}
}
if(flag)
{
maxSide = max(maxSide ,k+1);
}else{
break;
}
}
}
}
}
int maxSquare = maxSide * maxSide;
return maxSquare;
}
};
方法二:动态规划——定义一个二维 dp 数组,其中 dp[i][j] 表示满足题目条件的、以 (i, j) 为右下角的正方形或者长方形的属性。
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
if(m == 0 || n == 0)
{
return 0;
}
int maxSide = 0;
vector<vector<int>> dp(m,vector<int>(n));
//dp[i][j]表示一(i,j)为右下角的正方向或者长方形的边长
for(int i = 0; i < m;i++)
{
for(int j = 0; j < n;j++)
{
if(matrix[i][j] == '1')
{
if(i == 0||j == 0)
{
dp[i][j] = 1;
}else{
dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]) , dp[i-1][j-1]) + 1;
}
maxSide = max(maxSide,dp[i][j]);
}
}
}
int maxSquare = maxSide * maxSide;
return maxSquare;
}
};