leetcode中常见动态规划题目
买卖股票的最佳时机
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股股票。你也可以先购买,然后在 同一天出售。返回你能获得的最大利润 。
题解思路:
在第i天有两种状态,持有股票或者不持有股票,所以定义状态 d p [ i ] [ 0 ] dp[i] [0] dp[i][0]:第i天无股票所能够获得的最大利润, d p [ i ] [ 1 ] dp[i][1] dp[i][1]:第i天有股票。该问题就在于求解 m a x ( d p [ n ] [ 0 ] ) max(dp[n][0]) max(dp[n][0])。
第i天无股票所获得的最大利润只有两种情况,第i-1天无股票或者在第i-1天有股票而在第i天将股票卖出,状态转移方程如下:
d
p
[
i
]
[
0
]
=
m
a
x
{
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
}
dp[i][0] = max\{dp[i-1][0],dp[i-1][1]+prices[i] \}
dp[i][0]=max{dp[i−1][0],dp[i−1][1]+prices[i]}
第i天有股票所获得的最大利润只有两种情况,第i-1天有股票或者在第i天买入,状态转移方程如下:
d
p
[
i
]
[
1
]
=
m
a
x
{
d
p
[
i
−
1
]
[
1
]
,
d
p
[
i
−
1
]
[
0
]
−
p
r
i
c
e
s
[
i
]
}
dp[i][1] = max\{dp[i-1][1],dp[i-1][0]-prices[i]\}
dp[i][1]=max{dp[i−1][1],dp[i−1][0]−prices[i]}
定义出口:
d
p
[
0
]
[
0
]
=
0
,
d
p
[
0
]
[
1
]
=
−
p
r
i
c
e
s
[
0
]
.
dp[0][0] =0,dp[0][1]=-prices[0].
dp[0][0]=0,dp[0][1]=−prices[0].
AC代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int dp[prices.size()][2];
dp[0][0]=0;
dp[0][1]=-prices[0];
for (int i=1;i<prices.size();i++){
dp[i][0]=max(dp[i-1][1]+prices[i],dp[i-1][0]);
dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
}
return dp[prices.size()-1][0];
}
};
跳跃游戏
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
opt[i]表示到达下标i所需要的最短步长,opt[i]的值就等于在所有能到达下标i的位置k中,选择一个最小的opt[k]+1得到opt[i]。状态转移方程:
o
p
t
[
i
]
=
m
i
n
{
o
p
t
[
i
−
j
]
+
1
}
对
所
有
满
足
n
u
m
s
[
i
−
j
]
>
=
j
的
j
opt[i] = min \{ opt[i-j]+1\} \quad 对所有满足nums[i-j]>=j的j
opt[i]=min{opt[i−j]+1}对所有满足nums[i−j]>=j的j
出口opt[0] = 0
AC代码:
class Solution {
public:
int jump(vector<int>& nums) {
int dp[nums.size()+1];
for (int i=0;i<nums.size()+1;i++){
dp[i]=100000;
}
dp[0]=0;
for (int i=1;i<nums.size();i++){
for (int j=1;j<=i;j++){
if (nums[i-j]>=j){
if (dp[i-j]+1<dp[i]){
dp[i]=dp[i-j]+1;
}
}
}
}
return dp[nums.size()-1];
}
};
不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
d p [ i ] [ j ] dp[i][j] dp[i][j]表示从起点到坐标(i-1,j-1)的路径总数。机器人每次智能向下或者向右移动。
状态转移方程:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
]
[
j
−
1
]
f
o
r
i
>
0
&
&
j
>
0
d
p
[
i
]
[
0
]
=
1
f
o
r
i
≥
0
d
p
[
0
]
[
j
]
=
1
f
o
r
j
≥
0
dp[i][j] = dp[i-1][j] + dp[i][j-1] \quad for \quad i>0 \&\& j>0 \\ dp[i][0] = 1 \quad for \quad i \geq0 \\ dp[0][j] = 1 \quad for \quad j \geq0 \\
dp[i][j]=dp[i−1][j]+dp[i][j−1]fori>0&&j>0dp[i][0]=1fori≥0dp[0][j]=1forj≥0
AC代码:
class Solution {
public:
int uniquePaths(int m, int n) {
int dp[m][n];
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for (int j=1;j<n;j++){
dp[0][j]=dp[0][j-1];
}
for (int i=1;i<m;i++){
dp[i][0]=dp[i-1][0];
}
for (int i=1;i<m;i++){
for (int j=1;j<n;j++){
dp[i][j]=dp[i][j-1]+dp[i-1][j];
}
}
return dp[m-1][n-1];
}
};
不同路径2
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
状态转移方程与上面并无差别,但是需要注意:一旦遇到障碍点,需要将 d p [ i ] [ j ] dp[i][j] dp[i][j]置0
AC代码:
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int dp[obstacleGrid.size()][obstacleGrid[0].size()];
memset(dp,0,sizeof(dp));
if (obstacleGrid[0][0]==1){
return 0;
}
else{
dp[0][0]=1;
}
int flag1=1;
for (int j=1;j<obstacleGrid[0].size();j++){
if (obstacleGrid[0][j]==1){
flag1=0;
}
if (flag1==1){
dp[0][j]=1;
}
else{
dp[0][j]=0;
}
}
int flag2=1;
for (int i=1;i<obstacleGrid.size();i++){
if (obstacleGrid[i][0]==1){
flag2=0;
}
if (flag2==1){
dp[i][0]=1;
}
else{
dp[i][0]=0;
}
}
for (int i=1;i<obstacleGrid.size();i++){
for (int j=1;j<obstacleGrid[0].size();j++){
if (obstacleGrid[i][j]==1){
dp[i][j]=0;
}
else{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
int res=dp[obstacleGrid.size()-1][obstacleGrid[0].size()-1];
return res;
}
};
最小路径和
给定一个包含非负整数的 *m* x *n*
网格 grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
**说明:**每次只能向下或者向右移动一步。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示到达坐标(i-1,j-1)的最小数字总和。
状态转移方程:
d
p
[
i
]
[
j
]
=
m
i
n
{
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
}
+
g
r
i
d
[
i
]
[
j
]
f
o
r
i
>
0
&
&
j
>
0
d
p
[
i
]
[
0
]
=
d
p
[
i
−
1
]
[
0
]
+
g
r
i
d
[
i
]
[
0
]
f
o
r
i
>
0
d
p
[
0
]
[
j
]
=
d
p
[
0
]
[
j
−
1
]
+
g
r
i
d
[
0
]
[
j
]
f
o
r
j
>
0
dp[i][j] = min\{ dp[i-1][j] ,dp[i][j-1]\} + grid[i][j] \quad for \quad i>0 \&\& j>0 \\ dp[i][0] = dp[i-1][0] + grid[i][0] \quad for \quad i > 0 \\ dp[0][j] = dp[0][j-1] + grid[0][j] \quad for \quad j > 0 \\
dp[i][j]=min{dp[i−1][j],dp[i][j−1]}+grid[i][j]fori>0&&j>0dp[i][0]=dp[i−1][0]+grid[i][0]fori>0dp[0][j]=dp[0][j−1]+grid[0][j]forj>0
d
p
[
0
]
[
0
]
=
g
r
i
d
[
0
]
[
0
]
dp[0][0] = grid[0][0]
dp[0][0]=grid[0][0]
AC代码:
class Solution {
public:
int min(int a,int b){
if (a<b){
return a;
}
else{
return b;
}
}
int minPathSum(vector<vector<int>>& grid) {
int m=grid.size();
int n=grid[0].size();
int dp[m][n];
memset(dp,0,sizeof(dp));
dp[0][0]=grid[0][0];
for (int i=0;i<m;i++){
for (int j=0;j<n;j++){
if (i==0&&j==0)
continue;
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][j-1],dp[i-1][j])+grid[i][j];
}
}
}
int res=dp[m-1][n-1];
return res;
}
};
买卖股票最佳时期含冷冻期
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
新增状态: d p [ i ] [ 2 ] dp[i][2] dp[i][2]:表示第i天处于冷冻期
对于
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]:第i-1天处于冷冻期或者第i-1天无股票且不在冷冻期
d
p
[
i
]
[
0
]
=
m
a
x
{
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
2
]
}
dp[i][0] = max\{ dp[i-1][0],dp[i-1][2]\}
dp[i][0]=max{dp[i−1][0],dp[i−1][2]}
对于
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]:第i-1天有股票或者第i天买入
d
p
[
i
]
[
1
]
=
m
a
x
{
d
p
[
i
−
1
]
[
1
]
,
d
p
[
i
−
1
]
[
0
]
−
p
r
i
c
e
s
[
i
]
}
dp[i][1] = max\{ dp[i-1][1],dp[i-1][0]-prices[i]\}
dp[i][1]=max{dp[i−1][1],dp[i−1][0]−prices[i]}
对于
d
p
[
i
]
[
2
]
dp[i][2]
dp[i][2]:第i天卖出股票
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
1
]
+
p
r
i
c
e
s
[
i
]
dp[i][2] = dp[i-1][1]+prices[i]
dp[i][2]=dp[i−1][1]+prices[i]
结果: max{
d
p
[
n
]
[
0
]
,
d
p
[
n
]
[
2
]
dp[n][0],dp[n][2]
dp[n][0],dp[n][2]}
AC代码:
class Solution {
public:
int Max(int a,int b,int c){
int k=max(a,b);
int j=max(b,c);
return max(k,j);
}
int maxProfit(vector<int>& prices) {
int len=prices.size();
int dp[len][3];
memset(dp,0,sizeof(dp));
dp[0][0]=0;
dp[0][1]=-prices[0];
dp[0][2]=0;
for (int i=1;i<len;i++){
dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
dp[i][2]=dp[i-1][1]+prices[i];
dp[i][0]=max(dp[i-1][0],dp[i-1][2]);
}
return max(dp[len-1][0],dp[len-1][2]);
}
};