leetcode股票买卖问题【六连杀】

一次交易,找到买点最低,卖点最高即可。但限制条件是:卖点必须在买点之后。

所以很适合栈来操作,遇到比栈顶小的元素,就入栈,否则,将当前元素减去栈顶元素,然后不断更新最大值即可。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0){
            return 0;
        }
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(prices[0]);
        int max = 0;
        for (int i=1;i<prices.length;i++){
            if (prices[i] <= stack.peek()){
                stack.push(prices[i]);
            }else{
                max = Math.max(max, prices[i] - stack.peek());
            }
        }

        return max;
    }
}

优化建议:当然也可以用一个指针来记录栈顶元素,即遍历过程中遇到的更小的值,来优化空间。

这个没有交易次数的限制,只要卖点比买点高,就是赚了。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0){
            return 0;
        }
        int max = 0;
        for (int i=1;i<prices.length;i++){
            if (prices[i] > prices[i-1]){
                max += (prices[i] - prices[i-1]);
            }
        }

        return max;
    }
}

这个限制条件在于最多只能完成两笔交易,也就是说可以不交易,可以1次交易,可以2次交易。

其实可以理解为[3,3,5,0,0,3,1,4],从中间加个隔板,分成[3,3,5|||0,0,3,1,4],|||左边是第一次交易,右边是第二次交易,两次交易的最大利润即为所求。极端情况下,会退化成0次交易(最左边)和1次交易(最右边),我们需要做的就是尝试一下隔板具体在哪个位置的时候,能获得交易的最大值。

1.隔板左右都是进行一场交易,求最大值,就和121题一样了,外面需要加个循环,不断更新隔板的位置,然后不断更新最大值即可。

2.还有一种解法,就是可以从左往后扫描一遍,存下每个位置对应的一次交易获得的最大利润,相当于是隔板左边的交易最大值,隔板右边交易的最大值,可以从右边往左边遍历,然后求得每个位置对应的一次交易获得的最大值,然后将两个数组中对应位置元素夹起来就是该位置放置隔板,两次交易获得的最大值,求和后数组中的最大值即为最大利润。

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0){
            return 0;
        }
        int n = prices.length;
        int[] arr = new int[n];
        int idx = 0;
        for(int i=1;i<n;i++){
            if(prices[i] <= prices[idx]){
                idx = i;
            }
           arr[i] = Math.max(arr[i-1], prices[i] - prices[idx]);
        }
        int max = arr[n-1];
        idx = n - 1;
        int m = 0;
        for(int i=n-2;i>=0;i--){
            if(prices[i] >= prices[idx]){
                idx = i;
            }
            m = Math.max(m, prices[idx] - prices[i]);
            arr[i] += m;
            max = Math.max(max, arr[i]);
        }
        return max;
    }
}

这道题可以多次交易,但多了个条件,冷冻期限制。看了一些题解,许多都是将状态划分为3个,来处理的,不过我感觉还是两个好理解一点。其实无论是不是冷冻期,对于某一天来说,只有两个状态,持有股票和不持有股票,冷冻期的时候,肯定是前一天卖出股票了,而后一天不能买入,肯定也是不持有状态。

所以可以定义

hold[i]:第i天持有股票的最大收益;

unhold[i]:第i天未持有股票的最大收益。

很容易想到转移方程:

hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);------1

unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);------2

但1式其实有点问题,因为: unhold[i-1] = Math.max(unhold[i-2], hold[i-2] + prices[i-1]); 如果unhold[i-1]取值是hold[i-2] + prices[i-1],说明第i-2天持有股票,第i-1天给卖出去了,那么第i天就是冷冻期,就不能参与买入交易了。所以unhold[i-1]必须取值为unhold[i-2]才能在第i天买入。

于是状态方程就变成了:

hold[i] = Math.max(hold[i-1], unhold[i-2] - prices[i]);

unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if (n < 2){
            return 0;
        }
        int[] hold = new int[n];
        int[] unhold = new int[n];
        hold[0] = -prices[0];
        hold[1] = Math.max(-prices[0], -prices[1]);
        unhold[1] = Math.max(0, prices[1] - prices[0]);

        for (int i = 2; i < n; i++) {
            hold[i] = Math.max(hold[i-1], unhold[i-2] - prices[i]);
            unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i]);
        }

        return Math.max(hold[n-1],unhold[n-1]);
    }
}

同样是dp,状态由两个数组来维护:

hold[i]:第i天持有股票的最大收益;

unhold[i]:第i天未持有股票的最大收益。

状态转移方程:

hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);

unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i] - fee);

交易的时候需要扣除手续费。

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if (prices == null || prices.length == 0){
            return 0;
        }
        int n = prices.length;
        int[] hold = new int[n];
        int[] unhold = new int[n];
        hold[0] = -prices[0];
        for (int i=1;i<n;i++){
            hold[i] = Math.max(hold[i-1], unhold[i-1] - prices[i]);
            unhold[i] = Math.max(unhold[i-1], hold[i-1] + prices[i] - fee);
        }

        return Math.max(unhold[n-1], hold[n-1]);
    }
}

这个是在前面2笔交易的基础上做了扩展,变成k笔交易了。所以如果使用dp的话,笔数也需要作为状态参数考虑。

hold[k][i]:表示第i天第k次持有

unhold[k][i]:表示第i天第k次不持有

状态转移方程:

hold[i][j] = Math.max(hold[i][j-1], unhold[i-1][j-1] - prices[j]);

unhold[i][j] = Math.max(unhold[i][j-1], hold[i][j-1] + prices[j]);

初始化的时候,需要分析hold[i][0]、hold[0][i]、unhold[i][0]、unhold[0][i]的情况

class Solution {
    public int maxProfit(int k, int[] prices) {
        int n = prices.length;
        if(n < 2 || k < 1){
            return 0;
        }
        int[][] hold = new int[n][n];
        int[][] unhold = new int[n][n];
        int ans = 0;

        //初始化
        int min = prices[0];
        for(int i=0;i<n;i++){
            min = Math.max(min ,prices[i]);
            hold[0][i] = -min;
        }

        int idx =0;
        for(int i=1;i<n;i++){
            if(prices[i] <= prices[idx]){
                idx = i;
            }
            unhold[0][i] = Math.max(unhold[0][i-1], prices[i] - prices[idx]);
            ans = Math.max(ans, unhold[0][i]);
        }

        for(int i=1;i<k;i++){
            hold[i][0] = -1001;
            unhold[i][0] =  -1001;
        }

        for(int i=1;i<k;i++){
            for(int j = 1;j<n;j++){
                hold[i][j] = Math.max(hold[i][j-1], unhold[i-1][j-1] - prices[j]);
                unhold[i][j] = Math.max(unhold[i][j-1],hold[i][j-1] + prices[j]);
                ans = Math.max(ans, Math.max(hold[i][j], unhold[i][j]));
            }
        }

        return ans;

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值