首页 > 试题广场 >

买卖股票的最好时机(三)

[编程题]买卖股票的最好时机(三)
  • 热度指数:37568 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费

数据范围:,股票的价格满足
要求: 空间复杂度 ,时间复杂度
进阶:空间复杂度 ,时间复杂度
示例1

输入

[8,9,3,5,1,3]

输出

4

说明

第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2
第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2
总收益为4。              
示例2

输入

[9,8,4,1]

输出

0
示例3

输入

[1,2,8,3,8]

输出

12

说明

第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。
因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。        

备注:
总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
每一天我们都尽可能地买入最低价股票,并且尽可能地用最高价卖出。对于第二次交易,我们把第一次交易的盈利放入第二次交易的成本中(做差,第二次的成本减第一次的盈利prices[i]−sell1),那么我们第二次交易的的盈利就是两次交易的盈利。
    public int maxProfit (int[] prices) {
        // write code here
        int buy1 = Integer.MAX_VALUE;
        int buy2 = Integer.MAX_VALUE;
        int sell1 = 0;
        int sell2 = 0;

        for(int price : prices) {
            buy1 = Math.min(buy1,price);
            sell1 = Math.max(sell1,price-buy1);
            buy2 = Math.min(buy2,price - sell1); //在第二次买的时候,价格其实是考虑用了第一次赚的钱去补贴一部分的
            sell2 = Math.max(sell2,price - buy2);
        }
        return sell2;
    }


发表于 2023-07-23 19:22:17 回复(0)
初版动态规划,每一轮的状态:1.当前是第几天,2.当前处于第几次交易,3.当前的买入卖出情况
dp[i][j][k] 第i天,所处第j次交易,持有状态为k-0已买入,1已卖出,交易次数j-0第一次交易,1第二次交易
状态转移:
i天j次交易买入=max{  i-1天j-1次交易卖出后再买入第i天的价格      ,     i-1天j次交易买入  }    
       即:         dp[i][j][0]=Math.max(  dp[i-1][j-1][1]-prices[i],   dp[i-1][j][0]  );
i天j次交易卖出=max{  i-1天j次交易买入后再卖出第i天的价格    ,       i-1天j次交易卖出  }    
                dp[i][j][1]=Math.max(  dp[i-1][j][0]+prices[i],  dp[i-1][j][1]  );
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        int dp[][][] = new int[prices.length][2][2];
        // dp[i][j][k] 第i天,所处第j次交易,持有状态为k-0已买入,1已卖出,交易次数j-0,1

        // dp[i][0][0] 第i天,1次交易,持有1,买入,已买入1次
        // dp[i][0][1] 第i天,1次交易,持有0,卖出,已卖出一次
        // dp[i][1][0] 第i天,2次交易,持有1,买入,已买入2次
        // dp[i][1][1] 第i天,2次交易,持有0,卖出,已卖出2次
        dp[0][0][0]= -1*prices[0];//初始化第一天买入一次
        dp[0][1][0]= -1*prices[0];//初始化不可能买入第二次,所以设极小值
        for(int i = 1; i< prices.length;i++){
            for(int j=0;j<2;j++){
                dp[i][j][0]=Math.max((j==0?0:dp[i-1][j-1][1])-prices[i],dp[i-1][j][0]);
                dp[i][j][1]=Math.max(dp[i-1][j][0]+prices[i],dp[i-1][j][1]);
            }   
        }
        return dp[prices.length-1][1][1];
    }
}

优化空间:
由于天只与前一天状态相关,并且交易次数维度与买入卖出维度为固定的2x2,直接展开为1x4,即用四个变量表示即可,为了在循环中避免赋值影响记录上一轮的结果,还需拓展两个变量保存上一轮第一次买入和第二次买入的值;

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {//空间复杂度O(1),时间复杂度O(n)
        int firstBuy =-prices[0];// 截止今天,已买入1次
        int firstSell =0;// 截止今天,已卖出1次
        int secBuy =-prices[0];// 截止今天,已买入2次
        int secSell =0;// 截止今天,已卖出2次
        int preFirstBuy;//记录上一天中第一次已买入
        int preSecBuy;//记录上一天中第二次已买入
        for(int i = 1; i< prices.length;i++){
            preFirstBuy = firstBuy;
            preSecBuy = secBuy;
            firstBuy = Math.max(-prices[i],preFirstBuy);
            secBuy = Math.max(firstSell-prices[i],secBuy);
            firstSell = Math.max(preFirstBuy+prices[i],firstSell);
            secSell = Math.max(preSecBuy+prices[i],secSell);

        }
        return secSell;
    }
}




发表于 2023-04-17 11:28:56 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        //0   1第一次买入  2第一次卖出 3第二次买入 4第二次卖出 
        if(prices==null||prices.length==1) return 0;
        int[][] dp=new int[prices.length][5];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        dp[0][2]=0;
        dp[0][3]=-prices[0];
        dp[0][4]=0;
        for(int i=1;i<prices.length;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]);
            dp[i][4]=Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return Math.max(dp[prices.length-1][2],dp[prices.length-1][4]);
    }
}

发表于 2023-04-12 20:57:43 回复(0)
为什么编译失败呀?,帮帮孩子吧
发表于 2022-09-25 20:16:31 回复(0)
参数改成ArrayList<Integer> prices,再按照这个做就不编译报错了
发表于 2022-09-25 17:26:06 回复(0)
感觉是牛客这道题出了问题,同代码在力扣ac,在这里编译不过
思路非常复杂,我自己也说不清,尝试说说明一下:
首先,动态规划,四个状态 :分别是 1.第一次交易手里有股票,2.第一次交易手里无股票,3.第二次交易手里有股票,4.第二次交易手里无股票
dp【len】[4]
那么问题来到了状态转移与初始化
1.第一次交易手里有股票,可以分为两种情况,一个是之前买的还没有卖,一种是刚买的。由状态1,或者现买得到
2.第一次交易手里无股票,一种是之前手里就没有,一种是之前买过今天卖了。由状态1,或者2得到
3.第二次交易手里有股票,只可能是之前进行过卖过一次的状态得到,或者之前已经买过了。由状态2,或者3得到.
4.第二次交易手里无股票,一种是之前手里就没有,一种是之前买过今天卖了。由状态3,或者4得到.

之后是状态初始化。这里还需要解决一个问题就是,如何同时考虑只进行了一次买卖的情况,本题目选择将,第一次购买与第二次购买做同样的初始化,这样的话一个词在第一次交易期间保持同步,在第二次期间,第二次交易会优于第一次交易,这样通过比较会自动保留第二次的结果。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public static int maxProfit(int[] prices) {
        // write code here
        int[][] dp = new int[prices.length][4];
        for (int i = 0; i < prices.length; i++) {
            if (i == 0) {
                dp[0][0] = -prices[0];
                dp[0][1] = 0;
                dp[0][2] = -prices[0];
                dp[0][3] = 0;
            } else {
                dp[i][0] = Math.max(-prices[i], dp[i - 1][0]);
                dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
                dp[i][2] = Math.max(dp[i - 1][1] - prices[i], dp[i - 1][2]);
                dp[i][3] = Math.max(dp[i - 1][2] + prices[i], dp[i - 1][3]);
            }

        }
        
        return dp[prices.length - 1][3];
    }
}


发表于 2022-09-24 19:01:13 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int len = prices.length;
        int[][][] dp = new int[len][2][2];
        dp[0][0][0] = 0;
        dp[0][0][1] = 0;
        dp[0][1][0] = -prices[0];
        dp[0][1][1] = -prices[0];
        for(int i = 1;i < len; i++){
            dp[i][1][0] = Math.max(dp[i - 1][1][0],-prices[i]); //第一次持有
            dp[i][0][0] = Math.max(dp[i - 1][1][0]+prices[i],dp[i - 1][0][0]);//第一次没持有
            dp[i][1][1] = Math.max(dp[i - 1][0][0]-prices[i],dp[i - 1][1][1]);//第二次持有
            dp[i][0][1] = Math.max(dp[i - 1][1][1]+prices[i],dp[i - 1][0][1]);//第二次没持有
        }
        return dp[len - 1][0][1];
    }
}

发表于 2022-05-09 18:01:50 回复(0)
public int maxProfit (int[] prices) {
        int dp[]=new int[4];
        dp[0]=-prices[0];//1th purchase' left on 1th day
        dp[1]=0;//1th sell' earn on 1th day
        dp[2]=-prices[0];//2th purchase' left on 1th day
        dp[3]=0;//2th sell' earn on 1th day
        //Comparing with the max price of the previous day and the price of the current day
        for(int i=0;i<prices.length;i++){
            dp[0]=Math.max(dp[0],-prices[i]);//1th purchase' left on ith day
            dp[1]=Math.max(dp[1],prices[i]+dp[0]);//1th sell' earn on ith day
            dp[2]=Math.max(dp[2],dp[1]-prices[i]);//2th purchase' left on ith day
            dp[3]=Math.max(dp[3],prices[i]+dp[2]);//2th sell' earn on ith day
        }
        return dp[3];
    }

发表于 2022-04-25 16:40:51 回复(1)
三次遍历
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int first_max=0;
        int second_max=0;
        int third_max=0;
        int min=prices[0];
        int idx_min=0;
        int idx_max=0;
        //第一次遍历
        for(int i=1;i<prices.length;i++){
            int num = prices[i]-min;
            if(num > first_max){
                idx_max = i;
                first_max = num;
            }
            
            if(prices[i] < min){
                idx_min = i;
                min = prices[i];
            }
        }
        
        min = prices[0];
        //第二次遍历
        for(int i=1;i<=idx_min;i++){
            int num = prices[i]-min;
            second_max = Math.max(num, second_max);
            min = Math.min(min, prices[i]);
        }
        
        min = Integer.MAX_VALUE;
        //第三次遍历
        for(int i=idx_max;i<=prices.length-1;i++){
            int num = prices[i]-min;
            third_max = Math.max(num, third_max);
            min = Math.min(min, prices[i]);
        }
        
        return first_max + Math.max(second_max, third_max);
        
    }
}

发表于 2022-03-14 23:59:36 回复(0)
求解可以分出几个递增数组,手算没问题,测试过不去
import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int head=-1;
        int tail=-1;
        int profit=0;
        for(int i=0;i<prices.length-1;i++){
            if(prices[i]<prices[i+1]&&head==-1){
                head=i;  
            }
            if(prices[i]>prices[i+1]&&head!=-1){
                tail=i;
                profit=profit+prices[tail]-prices[head];
                head=-1;
                tail=-1;
            }
        }
        if(head!=-1){
            profit=profit+prices[prices.length-1]-prices[head];
        }
        return profit;
    }
}
发表于 2022-01-15 16:41:23 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
     //按照状态设置变量
     int n=prices.length;
     if(n==0||n==1) return 0;
     //初始化
     int fhost=-prices[0];
     int fnohost=0;
     int shost=-prices[0];
     int snohost=0;
     for(int i=1;i<n;i++){
         fhost=Math.max(fhost,-prices[i]);
         fnohost=Math.max(fnohost,fhost+prices[i]);
         shost=Math.max(shost,fnohost-prices[i]);
         snohost=Math.max(snohost,shost+prices[i]);
     }
     return snohost>0?snohost:0;
    }
}
发表于 2022-01-01 17:56:08 回复(0)
public int maxProfit (int[] prices) {
    if(prices.length == 0) return 0;
    int b1 = -prices[0], b2 = -prices[0];
    int s1 = 0, s2 = 0;
    for(int i = 0; i < prices.length; i++){
        b1 = Math.max(b1, -prices[i]);
        s1 = Math.max(s1, prices[i] + b1);
        b2 = Math.max(b2, s1 - prices[i]);
        s2 = Math.max(s2, prices[i] + b2);
    }
    return s2;
}

发表于 2021-10-11 17:44:15 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        int n=prices.length;
        int buy1=Integer.MIN_VALUE,buy2=Integer.MIN_VALUE;
        int sell1=0,sell2=0;
        for(int curPrice:prices){
            // buy1  记录 当前买入的最少花费 -cost ;
            // sell1 记录 交易一次的最大收益值 max_profit1 ;
            // buy2  记录 交易一次的收益-当前买入的最大值 ;
            // sell2 记录 交易两次的最大收益值 max_profit2 ;
            buy1=Math.max(buy1,(-1)*curPrice);
            sell1=Math.max(sell1,curPrice+buy1);
            buy2=Math.max(buy2,sell1-curPrice);
            sell2=Math.max(sell2,curPrice+buy2);
            System.out.println("buy1:" + buy1 + "    sell1:" +sell1+ "     buy2:"+ buy2 +  "    sell2:" + sell2);
        }
        return sell2;
// =========================================================
//         buy1:-8    sell1:0     buy2:-8    sell2:0
//         buy1:-8    sell1:1     buy2:-8    sell2:1
//         buy1:-3    sell1:1     buy2:-2    sell2:1
//         buy1:-3    sell1:2     buy2:-2    sell2:3
//         buy1:-1    sell1:2     buy2:1     sell2:3
//         buy1:-1    sell1:2     buy2:1     sell2:4
// =========================================================     
    }
}
发表于 2021-07-31 23:31:39 回复(0)
import java.util.*;
//https://www.bilibili.com/video/BV1To4y1d7TA?from=search&seid=13789178051737712436
public class Solution {
    public int maxProfit (int[] prices) {
        int[][] dp=new int[prices.length][5];
        dp[0][1]=-prices[0];
        dp[0][3]=-prices[0];
        for (int i=1;i<prices.length;i++){
            dp[i][0]=dp[i-1][0];
            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]);
            dp[i][4]=Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[prices.length-1][4];
    }
}


发表于 2021-07-19 22:26:51 回复(1)
    public int maxProfit (int[] prices) {
        int n=prices.length;
        int buy1=Integer.MIN_VALUE,buy2=Integer.MIN_VALUE;
        int sell1=0,sell2=0;
        for(int p:prices){//之前最大卖出价格,和在当天卖出的价格进行对比
            buy1=Math.max(buy1,(-1)*p);
            sell1=Math.max(sell1,p+buy1);
            buy2=Math.max(buy2,sell1-p);
            sell2=Math.max(sell2,p+buy2);
        }
        return sell2;
    }

发表于 2021-03-27 09:45:30 回复(4)
public int maxProfit (int[] prices) {
        // write code here
        int buy1 = Integer.MIN_VALUE, sell1 = 0;
        int buy2 = Integer.MIN_VALUE, sell2 = 0;
        for(int price : prices) {
            buy1 = Math.max(buy1, 0 - price);
            sell1 = Math.max(sell1, buy1 + price);
            buy2 = Math.max(buy2, sell1 - price);
            sell2 = Math.max(sell2, buy2 + price);
        }
        return sell2;
}
发表于 2021-02-19 15:33:24 回复(0)
动态规划:

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        // 买卖两次,需要使用到三元数组
        
        if(prices.length == 0) {
            return 0;
        }
        
        // 0表示不持有,1表示持有
        // dp[天数][交易的次数][持有的状态]
        int[][][] dp = new int[prices.length][3][2];
        
        // 初始化
        for(int i = 0;i <= 2;i++) {
            // 不持有的时候 为0
            dp[0][i][0] = 0;
            // 持有的时候,为第一天的值
            dp[0][i][1] = -prices[0];
        }

        for(int i = 1;i < prices.length;i++) {
            for(int k = 0;k <= 2;k++) {
                // 交易0次
                if(k == 0) {
                    // 不可能的值,赋值为MIN_VALUE
                    dp[i][k][1] = Integer.MIN_VALUE;
                    dp[i][k][0] = 0;
                    continue;
                }
                dp[i][k][0] = Math.max(dp[i-1][k][0],dp[i-1][k][1] + prices[i]);
                dp[i][k][1] = Math.max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i]); 
            }
        }
        return dp[prices.length - 1][2][0];
    }
}


发表于 2021-01-04 21:38:13 回复(2)