5.27 买卖股票 + 贪心区间

This commit is contained in:
zhangsan 2025-05-27 09:57:47 +08:00
parent 435ab1b93d
commit 620e720dba
6 changed files with 214 additions and 6 deletions

View File

@ -10,7 +10,7 @@ package dynamic_programming;
输出2 输出2
解释在第 1 (股票价格 = 2) 的时候买入在第 2 (股票价格 = 4) 的时候卖出这笔交易所能获得利润 = 4-2 = 2 解释在第 1 (股票价格 = 2) 的时候买入在第 2 (股票价格 = 4) 的时候卖出这笔交易所能获得利润 = 4-2 = 2
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/ * 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/
*/ */
//不会 //不会
public class MaxProfit4 { public class MaxProfit4 {

View File

@ -0,0 +1,60 @@
package dynamic_programming;
/**
* 题目 309. 买卖股票的最佳时机含冷冻期 (maxProfit)
* 描述给定一个整数数组prices其中第 prices[i] 表示第 i 天的股票价格
* 设计一个算法计算出最大利润在满足以下约束条件下你可以尽可能地完成更多的交易多次买卖一支股票:
* 卖出股票后你无法在第二天买入股票 (即冷冻期为 1 )
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
* 示例 1
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/
*/
//不会
public class MaxProfit5 {
public int maxProfit(int[] prices) {
int n = prices.length;
if (n == 0) return 0;
// dp[i][0]: 持有股票
// dp[i][1]: 不持有且不在冷冻期可买入/休息
// dp[i][2]: 今天卖出刚卖出
// dp[i][3]: 今天冷冻期昨天卖出
int[][] dp = new int[n][4];
// 0 天的初始状态
dp[0][0] = -prices[0]; // 买入
dp[0][1] = 0; // 空仓休息
dp[0][2] = 0; // 不可能刚卖出
dp[0][3] = 0; // 不可能处于冷冻期
// 从第 1 天开始状态转移
for (int i = 1; i < n; i++) {
int p = prices[i];
// 1. 持有股票要么延续持有要么从空仓/冷冻期买入
dp[i][0] = Math.max(
dp[i-1][0],
Math.max(dp[i-1][1] - p, dp[i-1][3] - p)
);
// 2. 不持有且不在冷冻期要么昨儿就休息要么昨儿冷冻期结束
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][3]);
// 3. 今天卖出一定是从持有状态卖出
dp[i][2] = dp[i-1][0] + p;
// 4. 今天冷冻期只能由昨天卖出进入
dp[i][3] = dp[i-1][2];
}
// 最后一天不持有股票的三种状态取最大
return Math.max(
Math.max(dp[n-1][1], dp[n-1][2]),
dp[n-1][3]
);
}
}

View File

@ -0,0 +1,34 @@
package dynamic_programming;
/**
* 题目 714. 买卖股票的最佳时机含手续费 (maxProfit)
* 描述给定一个整数数组 prices其中 prices[i]表示第 i 天的股票价格 整数 fee 代表了交易股票的手续费用
*
* 你可以无限次地完成交易但是你每笔交易都需要付手续费如果你已经购买了一个股票在卖出它之前你就不能再继续购买股票了
*
* 返回获得利润的最大值
*
* 注意这里的一笔交易指买入持有并卖出股票的整个过程每笔交易你只需要为支付一次手续费
* 示例 1
输入prices = [1, 3, 2, 8, 4, 9], fee = 2
输出8
解释能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
* 链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
*/
public class MaxProfit6 {
public int maxProfit(int[] prices, int fee) {
int[][]dp=new int[prices.length][2];
dp[0][0]=-prices[0]-fee;
for (int i = 1; i < prices.length; 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[prices.length-1][1];
}
}

View File

@ -0,0 +1,72 @@
package range;
import java.util.Arrays;
/**
* 题目 435. 无重叠区间 (eraseOverlapIntervals)
* 描述给定一个区间的集合 intervals 其中 intervals[i] = [starti, endi] 返回 需要移除区间的最小数量使剩余区间互不重叠
*
* 注意 只在一点上接触的区间是 不重叠的例如 [1, 2] [2, 3] 是不重叠的
* 示例 1
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 剩下的区间没有重叠
* 链接https://leetcode.cn/problems/non-overlapping-intervals/
*/
//不会
public class EraseOverlapIntervals {
/**
* 1.按结束时间排序
* 对所有区间按它们的右端点end从小到大排序
* 这样我们每次选取能最早结束的区间就能给后面留下最大的空间容纳更多不重叠的区间
*
* 2.一次线性扫描维护当前结束
* 用变量 end 记录上一次加入的未移除的区间的结束位置初始化为排序后第一个区间的结束
* count 记录当前保留下来的不重叠区间数量初始化为 1第一个区间肯定留下
* 从第二个区间开始遍历
* 如果当前区间的起点 start end说明它与上一次保留的区间不重叠可以保留
* count++并更新 end = 当前区间的 end
* 否则它与上一次保留的区间发生重叠必须移除其中一个
* 为了让后续更容易接上我们应当移除结束得更晚的那个区间也就是保留结束更早的那个即当前排序中的那个已经在保留的区间此时不更新 end相当于跳过移除当前区间
*
* 3.计算需移除的数量
* 最终保留下来的区间数为 count总区间数为 n所以最少要移除 n - count
* @param intervals
* @return
*/
public int eraseOverlapIntervals(int[][] intervals) {
//0,3 2,5 4,7
int n = intervals.length;
if (n == 0) {
// 没有区间当然不需要移除
return 0;
}
// 1. 按照区间的结束时间end从小到大排序
Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1]));
// 2. 初始化保留第一个区间
int count = 1; // 当前保留的不重叠区间数量
int end = intervals[0][1]; // 最近一次保留区间的结束时间
// 3. 遍历其余区间
for (int i = 1; i < n; i++) {
int startI = intervals[i][0];
int endI = intervals[i][1];
if (startI >= end) {
// 不重叠可以保留
count++;
end = endI; // 更新最近一次保留区间的结束时间
}
// 否则重叠移除结束更晚的那个区间
// 因为我们是按 end 升序遍历的当前 intervals[i] end end
// 所以移除它即可无需更新 end
}
// 4. 最少移除数 = 总数 - 保留下来的数量
return n - count;
}
}

View File

@ -19,16 +19,41 @@ import java.util.HashMap;
* 链接https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/ * 链接https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/
*/ */
public class FindMinArrowShots { public class FindMinArrowShots {
/**
* 一种经典做法是先按照每个气球的 右端点 从小到大排序
* 然后从最左边的气球开始选它的右端点作为射箭位置这样能保证这支箭能戳破尽可能多的 覆盖 当前右端点的气球
* 跳过所有已经被这支箭打到的气球再对下一个未被覆盖的气球重复相同操作
* @param points
* @return
*/
public int findMinArrowShots(int[][] points) { public int findMinArrowShots(int[][] points) {
int n = points.length;
if (n == 0) {
// 没有气球当然不用射箭
return 0;
}
// 1. 按照区间的右端点从小到大排序
// 这样每次选当前最小的右端点作为射箭位置
// 能最大程度地覆盖后续尽可能多的气球
Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1])); Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));
int index=0,cnt=0; int cnt = 0; // 箭的数量
while(index<points.length){ int index = 0; // 当前未覆盖的气球下标
int temp=index;
while(index<points.length&&points[temp][1]>=points[index][0]) // 2. 遍历所有气球只要还有未戳破的就再射一支箭
index++; while (index < n) {
// 这一支箭射在 points[index][1] 当前未处理区间的最小右端
int shootPos = points[index][1];
cnt++; cnt++;
// 3. 跳过所有能够被这支箭戳破的气球
// 条件它们的左端 shootPos
while (index < n && points[index][0] <= shootPos) {
index++;
}
} }
return cnt; return cnt;
} }
} }

View File

@ -0,0 +1,17 @@
package dynamic_programming;
import org.junit.Test;
import static org.junit.Assert.*;
public class MaxProfit6Test {
@Test
public void maxProfit() {
int[]prices = {1,3,7,5,10,3};
int fee = 3;
MaxProfit6 maxProfit6 = new MaxProfit6();
int res=maxProfit6.maxProfit(prices,fee);
System.out.println(res);
}
}