5.24 买卖股票 贪心+动规

This commit is contained in:
zhangsan 2025-05-25 12:05:24 +08:00
parent 0dfaf19cf9
commit 435ab1b93d
5 changed files with 250 additions and 18 deletions

View File

@ -0,0 +1,54 @@
package dynamic_programming;
/**
* 题目 123. 买卖股票的最佳时机 III (maxProfit)
* 描述给定一个数组它的第 i 个元素是一支给定的股票在第 i 天的价格
*
* 设计一个算法来计算你所能获取的最大利润你最多可以完成 两笔 交易
*
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* 示例 1
输入prices = [3,3,5,0,0,3,1,4]
输出6
解释在第 4 股票价格 = 0的时候买入在第 6 股票价格 = 3的时候卖出这笔交易所能获得利润 = 3-0 = 3
随后在第 7 股票价格 = 1的时候买入在第 8 股票价格 = 4的时候卖出这笔交易所能获得利润 = 4-1 = 3
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
*/
//不会
public class MaxProfit3 {
/**
* 一天一共就有五个状态
*
* 没有操作 其实我们也可以不设置这个状态
* 第一次持有股票
* 第一次不持有股票
* 第二次持有股票
* 第二次不持有股票
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
int len = prices.length;
// 边界判断, 题目中 length >= 1, 所以可省去
if (prices.length == 0) return 0;
/*
* 定义 5 种状态:
* 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出
*/
int[][] dp = new int[len][5];
dp[0][1] = -prices[0];
// 初始化第二次买入的状态是确保 最后结果是最多两次买卖的最大利润
dp[0][3] = -prices[0];
for (int i = 1; i < len; i++) {
dp[i][1] = Math.max(dp[i - 1][1], -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]); //第二次持有股票
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]); //第二次不持有股票
}
return dp[len - 1][4];
}
}

View File

@ -0,0 +1,65 @@
package dynamic_programming;
/**
* 题目 188. 买卖股票的最佳时机 IV (maxProfit)
* 描述给你一个整数数组 prices 和一个整数 k 其中 prices[i] 是某支给定的股票在第 i 天的价格
* 设计一个算法来计算你所能获取的最大利润你最多可以完成 k 笔交易也就是说你最多可以买 k k
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* 示例 1
输入k = 2, prices = [2,4,1]
输出2
解释在第 1 (股票价格 = 2) 的时候买入在第 2 (股票价格 = 4) 的时候卖出这笔交易所能获得利润 = 4-2 = 2
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
*/
//不会
public class MaxProfit4 {
public int maxProfit(int k, int[] prices) {
// 如果没有价格数据无法交易利润为 0
if (prices.length == 0) return 0;
int n = prices.length;
// dp[i][j] i 天结束时处于状态 j时能获得的最大利润
// 状态 j 0 2*k
// j = 0 从未操作过可以看作初始状态利润为 0
// j = 2*t-1 t 次买入后持有股票的状态
// j = 2*t t 次卖出后空仓的状态
int[][] dp = new int[n][2 * k + 1];
// 初始化 0 天买入的那些状态
// 对于每一笔交易 t1 t k在第 0 买入手里就是 prices[0]
for (int t = 1; t <= k; t++) {
dp[0][2*t - 1] = -prices[0];
// dp[0][2*t] 默认是 0还没卖出自然利润为 0
}
// 从第 1 天开始逐日更新
for (int i = 1; i < n; i++) {
// 枚举交易次数
for (int t = 1; t <= k; t++) {
int buyState = 2*t - 1; // t 次买入后
int sellState = 2*t; // t 次卖出后
// 转移方程 1买入状态
// 要么继承昨天已经买入的状态 dp[i-1][buyState]
// 要么今天刚用上一次卖出的资金买入 dp[i-1][buyState-1] 扣除 prices[i]
dp[i][buyState] = Math.max(
dp[i-1][buyState],
dp[i-1][buyState - 1] - prices[i]
);
// 转移方程 2卖出状态
// 要么继承昨天已经卖出的状态 dp[i-1][sellState]
// 要么今天把第 t 次买入的股票卖出 dp[i-1][buyState] 上加上 prices[i]
dp[i][sellState] = Math.max(
dp[i-1][sellState],
dp[i-1][buyState] + prices[i]
);
}
}
// 答案 n-1 完成第 k 次卖出后的最大利润
return dp[n-1][2*k];
}
}

View File

@ -0,0 +1,52 @@
package dynamic_programming;
/**
* 题目 221. 最大正方形 (maximalSquare)
* 描述在一个由 '0' '1' 组成的二维矩阵内找到只包含 '1' 的最大正方形并返回其面积
*
示例 1
输入matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出4
* 链接https://leetcode.cn/problems/maximal-square/
*/
//不会
public class MaximalSquare {
/**
* 那么如何计算 dp 中的每个元素值呢对于每个位置 (i,j)检查在矩阵中该位置的值
*
* 如果该位置的值是 0 dp(i,j)=0因为当前位置不可能在由 1 组成的正方形中
*
* 如果该位置的值是 1 dp(i,j) 的值由其上方左方和左上方的三个相邻位置的 dp 值决定具体而言当前位置的元素值等于三个相邻位置的元素中的最小值加 1状态转移方程如下
*
* dp(i,j)=min(dp(i1,j),dp(i1,j1),dp(i,j1))+1
*
* 作者力扣官方题解
* 链接https://leetcode.cn/problems/maximal-square/solutions/234964/zui-da-zheng-fang-xing-by-leetcode-solution/
* 来源力扣LeetCode
* 著作权归作者所有商业转载请联系作者获得授权非商业转载请注明出处
* @param matrix
* @return
*/
public int maximalSquare(char[][] matrix) {
int maxSide = 0;
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return maxSide;
}
int rows = matrix.length, columns = matrix[0].length;
int[][] dp = new int[rows][columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
maxSide = Math.max(maxSide, dp[i][j]);
}
}
}
int maxSquare = maxSide * maxSide;
return maxSquare;
}
}

View File

@ -18,26 +18,22 @@ package greedy;
public class MaxProfit2 { public class MaxProfit2 {
//考虑复杂了 //考虑复杂了
public int maxProfit1(int[] prices) { public int maxProfit1(int[] prices) {
int prediff=0,curdiff=0,maxprofit=0,buyprice=0; int n = prices.length, profit = 0;
boolean flag=false; int i = 0;
for (int i = 1; i < prices.length; i++) { while (i < n - 1) {
curdiff=prices[i]-prices[i-1]; // 找到下一个最低点
if(!flag){ while (i < n - 1 && prices[i + 1] <= prices[i]) {
if(prediff<=0 &&curdiff>0){ i++;
flag=true;
buyprice=prices[i-1];
}
}else {
if(prediff>=0&&curdiff<0){
flag=false;
maxprofit+=prices[i-1]-buyprice;
}
} }
prediff=curdiff; int buy = prices[i];
// 找到对应的最高点
while (i < n - 1 && prices[i + 1] > prices[i]) {
i++;
}
int sell = prices[i];
profit += (sell - buy);
} }
if(flag) return profit;
maxprofit+=prices[prices.length-1]-buyprice;
return maxprofit;
} }
//收集正利润的区间就是股票买卖的区间而我们只需要关注最终利润不需要记录区间 //收集正利润的区间就是股票买卖的区间而我们只需要关注最终利润不需要记录区间
public int maxProfit(int[] prices) { public int maxProfit(int[] prices) {

View File

@ -0,0 +1,65 @@
package greedy;
/**
* 题目 123. 买卖股票的最佳时机 III (maxProfit)
* 描述给定一个数组它的第 i 个元素是一支给定的股票在第 i 天的价格
*
* 设计一个算法来计算你所能获取的最大利润你最多可以完成 两笔 交易
*
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* 示例 1
输入prices = [3,3,5,0,0,3,1,4]
输出6
解释在第 4 股票价格 = 0的时候买入在第 6 股票价格 = 3的时候卖出这笔交易所能获得利润 = 3-0 = 3
随后在第 7 股票价格 = 1的时候买入在第 8 股票价格 = 4的时候卖出这笔交易所能获得利润 = 4-1 = 3
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
*/
//不会 困难题
public class MaxProfit3 {
/**
* 举个小例子 prices = [1, 5, 2, 6] 4
*
* 左半段你能算出 left = [0, 4, 4, 5]
* 右半段
* i=2价格 2为起点未来最高是 6利润 = 62 = 4
* i=1价格 5为起点未来最高同样 6利润 = 65 = 1
* i=0价格 1为起点未来最高 6利润 = 61 = 5
* right= [5,1,4,0]
* 左半段维护minPrice 右半段维护maxPrice
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
int n = prices.length;
if (n < 2) return 0; // (1)
// 1. left[i]: 0i 天内最多一笔交易的最优收益
int[] left = new int[n];
int minPrice = prices[0]; // (2)
for (int i = 1; i < n; i++) {
minPrice = Math.min(minPrice, prices[i]); // (3) 更新最低买入价
left[i] = Math.max(left[i - 1], // (4) 不做新交易今天卖出
prices[i] - minPrice);
}
// 2. right[i]: in-1 天内最多一笔交易的最优收益
int[] right = new int[n + 1]; // (5) 右边界额外留 1用于 i=n-1 时访问
int maxPrice = prices[n - 1]; // (6)
for (int i = n - 1; i >= 0; i--) {
maxPrice = Math.max(maxPrice, prices[i]); // (7) 更新最高卖出价
right[i] = Math.max(right[i + 1], // (8) 不做新交易今天买入后再卖出
maxPrice - prices[i]);
}
// 3. 枚举切分点第一笔结束在 i第二笔从 i+1 开始
int ans = 0;
for (int i = -1; i < n; i++) { // (9)
int leftProfit = (i >= 0) ? left[i] : 0; // (10)
int rightProfit = (i + 1 < n) ? right[i + 1] : 0; // (11)
ans = Math.max(ans, leftProfit + rightProfit); // (12)
}
return ans; // (13)
}
}