LeetCode动态规划题(二)

本文深入探讨了股票交易中的多种算法策略,包括基本的买卖时机选择、包含冷却期的交易策略、考虑交易费用的情景以及复杂情况下的多次交易优化。通过动态规划等方法,详细解析了如何在不同条件下实现利润最大化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

股票交易

121 Best Time to Buy and Sell Stock

三种解决方法

    public static int maxProfit(int[] arr) {
        if (arr == null || arr.length < 2) return 0;
        int[] dp = new int[arr.length-1];
        dp[0] = arr[1]-arr[0];
        for (int i = 1; i < arr.length-1; i++) {
            dp[i] = Math.max(dp[i-1], arr[i+1]-arr[0]);
        }
        for (int row = 1; row < arr.length-1; row++) {
            dp[row-1] = Integer.MIN_VALUE;
            for (int column = row; column < arr.length-1; column++) {
                int max = Math.max(arr[column+1]-arr[row], dp[column-1]);
                dp[column] = Math.max(max, dp[column]);
            }
        }
        return dp[arr.length-2];
    }

    public static int maxProfit2(int[] arr) {
        if (arr == null || arr.length < 2) return -1;
        // 价格数组可以看成是升升降降的子数组组成,
        // 从最初的升序子数组开始,往后遇到升的就将高度相加,降的就减去相应高度,
        // 这样就能统计出这一段的最低与最高的差值,
        // 直到小于0代表这一段的结束,从下一个升序子数组开始继续统计
        int sum = 0, max = 0;
        for (int i = 1; i < arr.length; i++) {
            sum = Math.max(0, sum + arr[i] - arr[i - 1]);
            max = Math.max(sum, max);
        }
        return max;
    }

    public static int maxProfit3(int[] arr) {
        if (arr == null || arr.length < 2) return -1;
        // min 表示i之前最低的价格,arr[i]-min 代表i天卖出能获得的最大利润
        int max = 0, min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            max = Math.max(max, arr[i]-min);
            min = Math.min(min, arr[i]);
        }
        return max;
    }

309,Best Time to Buy and Sell Stock with Cooldown
包含冷却期

    public int maxProfit(int[] prices) {
        if(prices.length<2) return 0;
        int[] buy = new int[prices.length];
        int[] sell = new int[prices.length];
        buy[0]=-prices[0];
        buy[1]=Math.max(buy[0], -prices[1]);
        sell[0]=0;
        sell[1]=Math.max(0, buy[0]+prices[1]);
        for(int i =2; i<prices.length; i++){
            buy[i]=Math.max(buy[i-1], sell[i-2]-prices[i]);
            sell[i]=Math.max(sell[i-1], buy[i-1]+prices[i]);
            
        }
        return sell[prices.length-1];
        
    }

变量代替数组

    public int maxProfit(int[] prices) {
        int sell = 0, pre_sell = 0, buy = Integer.MIN_VALUE, pre_buy;
        for (int price : prices) {
            pre_buy = buy;
            buy = Math.max(buy, pre_sell-price);
            pre_sell = sell;
            sell = Math.max(sell, pre_buy+price);
        }
        return sell;
    }

714, Best Time to Buy and Sell Stock with Transaction Fee
需要交易费用的股票交易

    public int maxProfit(int[] prices, int fee) {
    	// 方法一
        // if(prices == null || prices.length < 2) return 0;
        // int[] buy = new int[prices.length];
        // int[] sell = new int[prices.length];
        // buy[0] = -prices[0];
        // buy[1] = Math.max(buy[0], -prices[1]);
        // sell[1] = Math.max(sell[0], buy[0]+prices[1]-fee);
        // for(int i = 2; i < prices.length; i++) {
        //     buy[i] = Math.max(buy[i-1], sell[i-1]-prices[i]);
        //     sell[i] = Math.max(sell[i-1], buy[i-1]+prices[i]-fee);
        // }
        // return sell[prices.length-1];
        
        int buy = -prices[0], sell = 0;
        for(int i = 1; i < prices.length; i++) {
            int pre_buy = buy;
            buy = Math.max(buy, sell-prices[i]);
            sell = Math.max(sell, pre_buy+prices[i]-fee);
        }
        return sell;
    }

123, Best Time to Buy and Sell Stock III

    public int maxProfit(int[] prices) {
        // if(prices == null || prices.length == 0) return 0;
        // int[] sell = new int[2];
        // int[] buy = new int[2];
        // buy[0] = buy[1] = -prices[0];
        // for (int i = 1; i < prices.length; i++) {
        //     int buy0 = buy[0];
        //     buy[0] = Math.max(buy[0], -prices[i]);
        //     int sell0 = sell[0];
        //     sell[0] = Math.max(sell[0], buy0+prices[i]);
        //     int buy1 = buy[1];
        //     buy[1] = Math.max(buy[1], sell0-prices[i]);
        //     sell[1] = Math.max(sell[1], buy1+prices[i]);
        // }
        // return sell[1];
        
		// 对上面代码进行优化
		// sell1 代表第一次交易在该位置卖出的最大值;sell2 同理代表第二次
		// buy1 代表第一次交易在该位置买进的最大值;buy2 同理代表第二次
        if(prices == null || prices.length == 0) return 0;
        int sell1 = 0, sell2 = 0, buy1 = Integer.MIN_VALUE, buy2 = Integer.MIN_VALUE;
        for(int price : prices) {
            sell2 = Math.max(sell2, buy2+price);
            buy2 = Math.max(buy2, sell1-price);
            sell1 = Math.max(sell1, buy1+price);
            buy1 = Math.max(buy1, -price);
        }
        return sell2;
    }

有个问题:题目说至多两次交易,也就是说存在一次交易大于两次交易的情况,那么代码中返回 sell2 能够包含一次交易的情况吗?可以,因为在一开始我们将 buy1 ,buy2 ,sell1, sell2 都设置相同的值,在一次交易大于两次交易的情况下 sell1 与 sell2 始终相等,为什么这么说?
我们可以将数组看作是升升降降的曲线,可以判定只有三种情况下才会出现一次交易大于两次交易的情况,分别是:单升,降升降,升降。总结下三者共有特征就是只包含一次升过程,该情况下sell1 与 sell2 始终相等。

188, Best Time to Buy and Sell Stock IV

    public int maxProfit(int k, int[] prices) {
        if (prices == null || k == 0) return 0;
        // 为了防止k过大导致的Memory Limit Exceeded
        // 数组可以看成是升升降降的曲线
        // 当k大于等于数组一半时,所谓最大利润就是所有升序曲线高度和
        if (k >= prices.length/2) return quick(prices);

        int[] buy = new int[k];
        int[] sell = new int[k];
        Arrays.fill(buy, Integer.MIN_VALUE);
        for(int price : prices) {
            sell[0] = Math.max(sell[0], buy[0]+price);
            buy[0] = Math.max(buy[0], -price);
            for(int i = 1; i < k; i++) {
                sell[i] = Math.max(sell[i], buy[i]+price);
                buy[i] = Math.max(buy[i], sell[i-1]-price);
            }
        }
        return sell[k-1];
    }

    private int quick(int[] prices) {
        int profit = 0, len = prices.length;
        for(int i = 1; i < len; i++) {
            if (prices[i] > prices[i-1]) profit += prices[i]-prices[i-1];
        }
        return profit;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值