算法复习-动态规划(4)
LeetCode No.188 股票
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入: [3,2,6,5,0,3], k = 2
输出: 7
解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
LeetCode上面有好几道股票的动态规划题目,实际上这些题目都差不多,只要我们掌握了最难的那个,后面基本没问题。
首先是状态变量的定义:dp[i] 最粗糙的就是这样子,dp[i]代表第i天的最大利润
但是实际写下去我们会发现,仅仅一维数组是没有用的,因为缺少了很多中间状态。比如第i天的利润无法直接从第i-1中获取,中间夹杂着是否持有股票,交易次数等的限制。所以接下来我们在状态变量上面增加几个标志dp[i][k][a]:i表示天数,k表示交易次数,a表示是否持有股票(持有为1反之为0)
接下来就是状态转移方程:
第i天没有持有股票的时候,存在两种情况:
- 第i-1天的时候就已经没有持有股票了
- 第i天的时候将股票抛售
即:dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i])
第i天持有股票的时候,同样存在两种情况:
- 第i-1天的时候持有股票
- 第i天的时候将购入股票,这时候因为发生了交易,所以应该转移的状态是前一天的k-1次交易时没有持有股票的状态
即:dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i])
代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
if(k<=0||prices.size()<=1)
{
return 0;
}
if(k>prices.size()/2)
{
return maxProfit(prices); //当输入的k过大的时候就已经转换为无限次交易的题目
}
vector<vector<vector<int> > > dp(prices.size(),vector<vector<int> >(k+1,vector<int>(2)));
int maxprofit=0;
for(int i=0;i<prices.size();i++)
{
for(int j=1;j<=k;j++)
{
if(i-1<0)
{
dp[i][j][0]=0;
dp[i][j][1]=-prices[i];
continue;
} //这里需要对一些状态进行过滤
dp[i][j][0]=max(dp[i-1][j][1]+prices[i],dp[i-1][j][0]);
dp[i][j][1]=max(dp[i-1][j-1][0]-prices[i],dp[i-1][j][1]);
maxprofit=max(maxprofit,dp[i][j][0]);
}
}
return maxprofit;
}
int maxProfit(vector<int>& prices) //无限制次数交易的题目代码
{
int dp[2][2],x;
dp[0][0]=0;
dp[0][1]=-prices[0];
for(int i=1;i<prices.size();i++)
{
x=i%2;
dp[x][0]=max(dp[!x][0],dp[!x][1]+prices[i]);
dp[x][1]=max(dp[!x][1],dp[!x][0]-prices[i]);
}
return dp[x][0];
}
};
感想
股票这边看了很久,反复的去看教程。事实上我的代码并不高效,只能是勉强过了,题解中大佬们基本都是几ms的,但是这个思路是最为朴素的动态规划,更适合刚入门学习动态规划的人。一开始并不是很能理解多维dp中有些维数的作用,事实上这些有些维数(最多应该也就3了吧…再多就得还算法了)起到的作用是用来分解状态的。例如股票这道题目,题目的限制条件有:交易k次,不能同时参与多个交易 =====> 意味着要抛售前必须有股票,交易次数必须小于k次(废话),但是如果只有一个dp[n]一维数组的大小,很难界定说上个状态是否有股票,交易次数现在是否小于k次了,动态规划的核心就是记忆化,保存每一次递推的结果用于下一次。但是题目中最初的状态都可以分为:持有股票、没有持有股票、交易了、没有交易,可以说并不唯一,所以需要多维度来帮助表达限制条件,最直观的就是0表示没有股票,1表示持有股票。