拿捏动态规划之买卖股票
作者:互联网
拿捏动态规划之买卖股票
动态规划五步走(From 代码随想录)
-
确定dp数组以及下标的含义
-
确定递推公式 即状态转移方程
-
初始化dp数组
-
确定遍历顺序:
以背包问题为例:先遍历背包 再遍历物品 则会出现拿多次相同物品,只是顺序不同 (序列数)
先遍历物品再遍历背包则不会出现 (组合数) -
举例推导dp数组
每次对结果有疑问的时候就把dp数组打印出来,看看跟自己想的是不是有出入
正片开始
买卖股票的最佳时机
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][0]含义:当前这一天持股的利润
// dp[i][1]含义:当前这一天不持股的利润
// 状态转移方程: dp[i][0] = Math.max(dp[i - 1][0], - prices[i]);
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
int[][] dp = new int[n][2];
// 初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], - prices[i]); // 找哪一天买入最便宜
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]); // 找哪一天卖出最贵
}
// 返回最后一天不持股的状态
return dp[n - 1][1];
}
}
买卖股票的最佳时机II
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// 与I的区别为可以买卖多次
// dp[i][0]含义: 当前这一天持有股票的利润
// dp[i][1]含义: 当前这一天不持有股票的利润
// 状态转移: dp[i][0] 在I题中因为只能买卖一次,所以只能从前面的dp[i - 1][0]和-prices[i]转移而来
// 在本题中可以买卖多次 dp[i][0] 可以从 dp[i - 1][0] 和 dp[i - 1][1] - prices[i] (即前面买卖的最大利润 - 当前买入的价格) 转移而来
// 所以 dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
int[][] dp = new int[n][2];
// 初始化
dp[0][0] = -prices[0];
dp[0][1] = 0; // 刚开始的一天,前面都没有持股,所以也没有办法卖出,利润初始为0
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
}
return dp[n - 1][1];
}
}
买卖股票的最佳时机III
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][0]: 第一次买入 dp[i][1]: 第一次卖出 dp[i][2]: 第二次买入 dp[i][3]:第二次卖出
// dp[i][2]的状态需要从dp[i - 1][1]转移而来 dp[i][3]的状态需要从dp[i - 1][2]转移而来
// 状态转移方程: dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
// dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] - prices[i]);
// dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] + prices[i]);
int[][] dp = new int[n][4];
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = dp[0][0];
dp[0][3] = dp[0][1];
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] - prices[i]);
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] + prices[i]);
}
return dp[n - 1][3];
}
}
买卖股票的最佳时机IV
class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
int kLen = k * 2;
if(n == 0 || k == 0) return 0;
// dp[i][j]含义:
// j为偶数(买入): 当前时间买入股票的利润
// j为奇数(卖出): 当前时间卖出股票的利润
// 状态转移方程: 1.前一天的状态继续不变 2.从前一天的 买入/卖出 状态变化为 卖出/买入
// dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] +/- prices[i]);
int[][] dp = new int[n][kLen];
// 初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
// 可以直接全部初始化为 -prices[0] 和 0 但是为了更好理解 用了一个循环
// 即 第1天第n次买入时的利润为 前n - 1次的利润 - 当前价格
// 第1天第n次卖出时的利润为 前n - 1次的利润 + 这次操作的利润
// 但是这些都在同一天发生,所以利润均为0
for(int i = 2; i < kLen; i++) {
dp[0][i] = dp[0][i - 2];
}
for(int i = 1; i < n; i++) {
for(int j = 0; j < kLen; j++) {
if(j == 0) dp[i][j] = Math.max(dp[i - 1][j], -prices[i]);
else if(j % 2 == 0) dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]); // 持有
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]); // 不持有
}
}
return dp[n - 1][kLen - 1];
}
}
最佳买卖股票时机含冷冻期
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][0] 当前持有股票的最大利润
// dp[i][1] 当前不持有股票(可购买)的最大利润
// dp[i][2] 当前不持有股票(在冷冻期)的最大利润
// dp[i][0] 只能从 1.前一天就是持有 2.前一天为冷冻期,这一天买入
// 所以 dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
// dp[i][1] 只能从 1.前一天就不持有股票 2.这一天将股票卖出
// dp[i][2] 说明前一天必须是卖出 所以只能从dp[i - 1][1]转移而来
// 状态转移方程: dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
// dp[i][2] = dp[i - 1][1];
int[][] dp = new int[n][3];
// 初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
dp[i][2] = dp[i - 1][1];
}
// 为什么直接返回dp[n - 1][1]: 因为有可能会在最后一天卖出, dp[n - 1][1]的利润永远是最高的
return dp[n - 1][1];
}
}
买卖股票的最佳时机含手续费
class Solution {
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
// dp[i][0] 表示当前持有股票的最大利润
// dp[i][1] 表示当前不持有股票的最大利润
// 状态转移方程(包括减去手续费): dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee);
// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
int[][] dp = new int[n][2];
// 初始化
dp[0][0] = -prices[0] - fee; // 第一次买入也需要手续费
dp[0][1] = 0;
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
}
return dp[n - 1][1];
}
}
标签:买卖,int,max,拿捏,Math,股票,prices,动态,dp 来源: https://blog.csdn.net/weixin_47645663/article/details/117445310