目录
1.第一题
画图分析:

使用动态规划解决问题
1.确定状态表示 根据经验+题目要求
dp[i]表示:选择到i位置时,最长的预约时长
但这里其实还可以再划分这个状态表示,选择到i位置时,对于i位置有选与不选两种情况
f[i]:表示选择到i位置时,i位置值(nums[i])必选,最长的预约时长
g[i]:表示选择到i位置时,nums[i]不选,最长的预约时长
2.状态转移方程

3.初始化
根据状态转移方程,发生越界的是f[0],g[0]
根据状态表示,f[0]表示选择到0位置,且nums[0]必选的最长时长 f[0]=nums[0]
同理,g[0]=0
4.填表顺序 从左往右两个表一起填
5.返回值
对于最后一个位置(n-1)有选与不选两种情况,即返回max(f[n-1],g[n-1])
具体代码
int massage(vector<int>& nums)
{
int n=nums.size();
if(n==0) return 0;//处理边界
vector<int> f(n),g(n);
f[0]=nums[0];
for(int i=1;i<n;++i)
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[n-1],g[n-1]);
}
打家劫舍问题
1.第二题
画图分析
使用动态规划来解决
1.确定状态表示
dp[i]:表示当偷到i位置的房屋时,此时的偷到的最高金额
但同样的对于i位置的处理,可以有偷与不偷两种情况
f[i]:表示偷到i位置时,偷i位置(nums[i]),此时的最高金额
g[i]:表示偷到i位置时,不偷nums[i],此时的最高金额
2.状态转移方程

3.初始化
根据状态表示,f[0]=nums[0],g[0]=0
4.填表顺序 从左往右两个表一起填
5.返回值
对于最后一个位置(n-1)有选与不选两种情况,即返回max(f[n-1],g[n-1])
具体代码
int rob(vector<int>& nums)
{
//1.创建dp表
//2.初始化
//3.填表
//4.返回值
int n=nums.size();
vector<int> f(n),g(n);
f[0]=nums[0];
for(int i=1;i<n;++i)
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[n-1],g[n-1]);
}
2.第三题
画图分析
对于这个问题,我们会发现对于上题(打家劫舍)明显不同的一点为第一个位置的区别
本题第一个位置偷后,最后一个位置绝对不能偷,
而对于上题最后一个位置可以偷也可以不偷两种情况
优化:
这里对于这点不同,我们可以单独拿出来研究一下

对于剩下的状态表示,状态转移方程等参考上题
具体代码
int rob(vector<int>& nums)
{
int n=nums.size();
return max(nums[0]+rob1(nums,2,n-2),rob1(nums,1,n-1));
}
int rob1(vector<int>& nums,int left,int right)
{
int n=nums.size();
//注意数据个数范围
if(left>right) return 0;
vector<int> f(n),g(n);
f[left]=nums[left];//因为开始位置不一定是0,初始化时需要注意
for(int i=left+1;i<=right;++i)
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[right],g[right]);//注意返回的是最后一个位置,这里是right
}
3.第四题
画图分析

对于遇到新问题时,我们可以尽量把新问题往我们熟悉的问题上进行转化

因此通过此处的预处理操作就可将此问题转化为"打家劫舍问题",对于动态规划的步骤参考上面
具体代码
int deleteAndEarn(vector<int>& nums)
{
//1.统计数组中每个数据的总和
//对于这里所要开的空间,可以参考数据范围[1,10000]
const int N=10001;
int count[N]={0};
for(auto x:nums) count[x]+=x;
//2.做一次打家劫舍问题
vector<int> f(N),g(N);
for(int i=1;i<N;++i)
{
f[i]=g[i-1]+count[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[N-1],g[N-1]);
}
5.第五题
画图分析
使用动态规划解决
1.状态表示
dp[i]表示粉刷到i号房子时的最少花费
而对于i号房子有刷红蓝绿三种情况,因此还可以再次划分状态表示,且使用二维的形式
dp[i][0]:粉刷到i号房子时,且i号房子使用红色粉刷时的最少花费
dp[i][1]:粉刷到i号房子时,且i号房子使用蓝色粉刷时的最少花费
dp[i][2]:粉刷到i号房子时,且i号房子使用绿色粉刷时的最少花费
2.状态转移方程

3.初始化
这里有三种的情况的话,就要初始化三个值的,分别为dp[0][0],dp[0][1],dp[0][2]
为了简化初始化过程,可以采用添加一个虚拟节点的
这是就需要关注虚拟节点中的值,要保证后续填表是正确的
还需要注意使用原数组时的下标映射关系

4.初始化 从左往右填三个表
5.返回值 因为最后一个房子有三种粉刷情况 min(dp[n][0],min(dp[n][1],dp[n][2]))
具体代码
int minCost(vector<vector<int>>& costs)
{
int n=costs.size();
vector<vector<int>> dp(n+1,vector<int>(3));
for(int i=1;i<=n;++i)
{
dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i-1][0];
dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i-1][1];
dp[i][2]=min(dp[i-1][0],dp[i-1][1])+costs[i-1][2];
}
return min(dp[n][0],min(dp[n][1],dp[n][2]));
}
买股票问题
1.第六题
OJ传送门 LeetCode<309> 买卖股票的最佳时机含冷冻期
画图分析

使用动态规划解决
1.确定状态表示
dp[i]表示到第i天时,获得的最高利润
但对于第i天具体所做的操作可以有买股票,卖出股票,冷冻期不做任何事,分为这三种
这里我们可以划分为三种状态的"买入","冷冻期(卖出股票后处于的状态)","可交易(手里没有股票,且没有处于冷静期)",为了方便表示可以采用n*3二维数组的形式表示
dp[i][0]:表示第i天结束后,处于买入状态时的最高利润
dp[i][1]:表示第i天结束后,处于可交易状态时的最高利润
dp[i][2]:表示第i天结束后,处于冷冻状态时的最高利润
2.状态转移方程
对于多状态之间可以相互转化的,可以采用状态机的方式进行分析

根据有效的箭头列出状态转移方程
根据箭头的数量来计算,统计每个状态的每一个箭头,dp[i-1][箭尾状态标识]+所做的操作
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][2])
dp[i][2]=dp[i-1][0]+prices[i]
3.初始化
根据状态转移方程,需要初始化的为dp[0][0],dp[0][1],dp[0][2]
根据状态表示
dp[0][0]表示第0天结束时,处于买入状态的最高利润
应该是第0天时买当天的股票,此时利润为-prices[0]
dp[0][1]表示第0天结束时,处于可交易状态的最高利润
应该是第0天时啥也没做,此时利润为0
dp[0][2]表示第0天结束时,处于冷冻状态的最高利润
应该是第0天时买当天的股票,又当天卖出了,此时利润为0
4.填表顺序
从左往右直接填三个表
5.返回值
当到最后一天结束时,是有三种状态的,每种状态对应一个最高利润,结果应该为三者取大
max(dp[n-1][0],dp[n-1][1],dp[n-1][2])
具体代码
int maxProfit(vector<int>& prices)
{
int n=prices.size();
vector<vector<int>> dp(n,vector<int>(3));
dp[0][0]=-prices[0];
for(int i=1;i<n;++i)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][2]);
dp[i][2]=dp[i-1][0]+prices[i];
}
return max(dp[n-1][0],max(dp[n-1][1],dp[n-1][2]));
}
2.第七题
OJ传送门 LeetCode<714>买卖股票的最佳时机含手续费
画图分析
使用动态规划解决
1.确定状态转移
dp[i]表示第i天结束后,所获得的最高利润
而对于本题第i+1天所处的状态有,买了股票的"买入状态",卖出股票的"可交易状态"
这里再对状态表示进行划分,可以采用N*2二维的表示,也可以采用两个函数f[i],g[i]表示
f[i]表示第i天结束之后,所处的为买入状态时的最高利润
g[i]表示第i天结束之后,所处的为可交易状态时的最高利润
2.根据状态机求状态转移方程

3.初始化
根据状态转移和状态转移方程
f[0]初始化为-prices[0],g[0]=0
4.填表顺序 从左往右一次填两个表
5.返回值 max(f[n-1],g[n-1]) 这里注意是当最后一天结束时处于是买入的话,一定不是最高利润,可以直接返回 g[n-1]
具体代码
int maxProfit(vector<int>& prices, int fee)
{
int n=prices.size();
vector<int> f(n),g(n);
f[0]=-prices[0];
for(int i=1;i<n;++i)
{
f[i]=max(f[i-1],g[i-1]-prices[i]);
g[i]=max(g[i-1],f[i-1]+prices[i]-fee);
}
return g[n-1];
}
3.第八题
OJ 传送门 LeetCode<123>买卖股票的最佳时机 III
画图分析

使用动态规划来解决
1.确定状态表示
dp[i]表示第i天结束时,获得的最高利润
但同样的对于第i天结束时所处的状态有买了股票的"买入状态"和卖出股票的“可交易状态”
对上述的状态表示再进行细分,采用两个函数f[i],g[i]表示,但对于本题还有个限制条件就是交易次数不超过两次,因此对买入和可交易状态还可以进行再细分,有当第i天结束时,完成0/1/2次交易的最高利润,这样的话就会有很多的状态
但为了减少状态表示的话,就可以再增加一维,最终的状态表示为
f[i][j]:表示当第i天结束后,完成了j次交易,且处于买入状态,此时的最高利润
g[i][j]:表示当第i天结束后,完成了j次交易,且处于可交易状态,此时的最高利润
2.状态转移方程

3.初始化
对于f[i][j]的状态转移方程,只需要初始化两个表的第一行就行
对于g[i][j]的状态转移方程,由于有f[i-1][j-1],所以需要初始化f表第一行和第一列
为了处理上述的初始化情况,我们可以通过调整状态转移方程来转化为只初始化第一行即可

4.填表顺序
从上往下填写每一行,每一行从左往右
5.返回值
返回g表最后一行的最大值
具体代码
const int FIM=0x3f3f3f3f;
int maxProfit(vector<int>& prices)
{
int n=prices.size();
vector<vector<int>> f(n,vector<int>(3,-FIM));
auto g=f;
f[0][0]=-prices[0];
g[0][0]=0;
for(int i=1;i<n;++i)
{
for(int j=0;j<3;++j)
{
f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);
g[i][j]=g[i-1][j];
if(j>=1)
g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
}
}
int ret=0;
for(int j=0;j<3;++j)
{
ret=max(ret,g[n-1][j]);
}
return ret;
}
4.第九题
OJ传送门 LeetCode<188>买卖股票的最佳时机IV
画图分析

其余的如状态表示,状态转移方程,填表顺序,返回值和上题一样
这里说一下初始化时的不同,因这里的k可能大于数据个数,这样在初始化时会浪费空间的,可以这样处理 k=min(k,n/2),两天交易一次的
在处理第一行时,f[0][0]依然是-p[0][0],g[0][0]=0,其余第一行的位置都填0x3f3f3f3f
具体代码
const int FIN=0x3f3f3f3f;
int maxProfit(int k, vector<int>& prices)
{
int n=prices.size();
k=min(k,n/2);//预处理
vector<vector<int>> f(n,vector<int>(k+1,-FIN));
auto g=f;
f[0][0]=-prices[0],g[0][0]=0;
for(int i=1;i<n;++i)
{
for(int j=0;j<=k;++j)
{
f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);
g[i][j]=g[i-1][j];
if(j>=1)//若状态存在
g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
}
}
int ret=0;
for(int j=0;j<=k;++j)
{
ret=max(g[n-1][j],ret);
}
return ret;
}

被折叠的 条评论
为什么被折叠?



