leetcode股票買賣問題

股票買賣問題

本人是大三學生,有問題歡迎及時指出呀

1.買賣股票的最佳時機

在這裏插入圖片描述

暴力法

暴力法很簡答,兩層循環遍歷,獲取每一種利潤,然後再找出最大利潤

    public int maxProfit(int[] prices) {
        int max = 0;
        //從第一個元素遍歷到倒數第二個元素
        for(int i = 0;i < prices.length-1;i++){
            //從第二個元素遍歷到最後一個元素,保證買入在賣出前
            for(int j = i+1;j < prices.length;j++){
                int profile = prices[j] - prices[i];
                if(profile > max){
                    max = profile;
                }
            }
        }
        return max;
    }
//暴力法
func maxProfit1(price []int)int{

	if len(price) == 0{
		return 0
	}
	max := 0
	//第一層循環控制買入價
	for i:= 0;i < len(price) - 1;i++{
		//第二層循環控制賣出價
		for j := i + 1;j < len(price);j++{
			temp := price[j] - price[i]
			max = int(math.Max(float64(temp), float64(max)))
		}
	}
	return max
}

動態規劃

動態規劃 前i天的最大收益 = max{前i-1天的最大收益,第i天的價格-前i-1天中的最小价格}

    public int maxProfitWithDp(int[] prices) {
        int min = Integer.MAX_VALUE;
        int max = 0;
        //找出買入價後的最大賣出價
        for(int i = 0;i<prices.length;i++){
            if(prices[i]<min){
                //找到最小買入價
                min = prices[i];
            }else if(prices[i] - min > max){
                //找出最大利潤
                max = prices[i] - min;
            }
        }
        return max;
    }
func maxProfit(prices []int) int {

	if len(prices) == 0{
		return 0
	}
	min := prices[0]
	max := 0
	for i := 1;i < len(prices);i++{
		//找出最小買入價
		min = int(math.Min(float64(min), float64(prices[i])))
		//找出最大利潤
		max = int(math.Max(float64(max),float64(prices[i] - min)))
	}
	return max
}

2.最佳買賣股票時機含冷凍期

在這裏插入圖片描述

與之前不同的點:

  • 可以完成多次交易
  • 每次賣出股票後需等待一天纔可以買入新股票
  • 買股票前需賣出原持有股票

這個問題中存在三種狀態:

  • 賣出(seld)
  • 買入(hold)
  • 持有股票(什麼也不幹,可能處於冷凍期也可能不處於冷凍期)(rest)

這三種狀態的狀態轉移是:

  1. 持有股票到賣出即hold到seld

    seld[i] = hold[i - 1] + price[i]
    
  2. 持有股票(可能前一天就持有,也可能處於冷凍期沒有然後過了冷凍期後買入變爲持有)

    hold[i] = max{hold[i - 1],rest[i - 1] - price[i]}
    
  3. 冷凍期(可能前一天就是冷凍期今天什麼也不幹,也可能前一天剛賣出,今天爲冷凍期)

    rest[i] = max{rest[i - 1],sold[i - 1]}
    

用profit[i]表示前i天的最大利潤

所以動態方程爲:

profit[i] = max{sold[i - 1],rest[i - 1]}
func maxProfit(prices []int) int {
	if len(prices) == 0 {
		return 0
	}
	hold := math.MinInt8
	seld := 0
	rest := 0
	for i := 0;i < len(prices);i++{
		//記錄前一天賣出狀態
		preSeld := seld
		//賣出狀態:前一天持有然後當天賣出
		seld = hold + prices[i]
		//持有狀態:前一天本來就持有,或者前一天冷凍期過了後當天買入
		hold = int(math.Max(float64(hold), float64(rest-prices[i])))
		//冷凍期狀態:前一天是冷凍期,或者前一天剛好賣出
		rest = int(math.Max(float64(rest), float64(preSeld)))
	}
	return int(math.Max(float64(rest), float64(seld)))
}
    public int maxProfit(int[] prices) {
        int sold = 0;
        int rest = 0;
        int hold = Integer.MIN_VALUE;
        for(int i : prices){
            //記錄前一天賣出
            int preSold = sold;
            //狀態轉移方程:
                // newHold = Max{oldHold,rest - price[i]}
                // newSold = hold + price[i]
                // newRest = Max{oldRest,oldSold}
            sold = hold + i;
            hold = Math.max(hold,rest - i);
            rest = Math.max(preSold,rest);
        }
        return Math.max(sold,rest);
    }

3.買賣股票的最佳時機含手續費

在這裏插入圖片描述

這道題目就沒有了冷凍期的概念,即賣出後可以立即買入,但是每一次交易(買入和賣出)都需要付一次手續費

這道題目我用一個二位數組來表示狀態:

dp[i][0]:第i天不持有股票的最大利潤
dp[i][1]:第i天持有股票的最大利潤

分析狀態轉移:

從第 i - 1天狀態到第 i 天 持有狀態:

  • i - 1天本來持有持有
  • i - 1天不持有但是第i天購買
dp[i] = max(dp[i - 1][1],dp[i - 1][0] - prices[i])

從第 i - 1天狀態到第 i 天 不持有狀態:

  • 第 i - 1天本來不持有
  • 第 i - 1天本來持有但是第 i 天賣出並且交上手續費
dp[i][0] = max(dp[i - 1][0],dp[i - 1][1] + prices[i] - fee)

所以,最終狀態方程:

            res = max(res,Math.max(dp[i][0],dp[i][1]))

分析初始狀態:

        dp[0][0] = 0;
        dp[0][1] = -prices[0];
	if len(prices) == 0{
		return 0
	}
	var dp [50001][2]int
	res := 0
	dp[0][0] = 0
	dp[0][1] = -prices[0]
	for i := 1;i < len(prices);i++{
		dp[i][0] = int(math.Max(float64(dp[i-1][0]), float64(dp[i-1][1] + prices[i] - fee)))
		dp[i][1] = int(math.Max(float64(dp[i - 1][1]),float64(dp[i - 1][0] - prices[i])))
		res = int(math.Max(float64(dp[i][0]),float64(dp[i][1])))
	}
	return res
    public int maxProfit(int[] prices, int fee) {
        if(prices.length == 0){
            return 0;
        }
        int[][] dp = new int[prices.length][2];
        int res = 0;
        dp[0][0] = 0;
        dp[0][1] = -prices[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][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i] - fee);
            res = Math.max(res,Math.max(dp[i][0],dp[i][1]));
        }
        return res;
    }

4.買賣股票的最佳時機 III

在這裏插入圖片描述

這道題目跟之前又有不同了,沒有了冷凍期,沒有了手續費,但是限制了交易次數,只能交易兩次

分析狀態:

dp[i][1][0]:第一次交易第i天賣出
dp[i][1][1]:第一次交易第i天買入
dp[i][2][0]:第二次交易第i天賣出
dp[i][2][1]:第二次交易第i天買入

狀態轉移分析:

dp[i][1][1] --》 dp[i][1][0] --》 dp[i][2][1] --》 dp[i][2][0]

所以:

func maxProfit(prices []int) int {
	firstBuy := math.MinInt64
	firstSeld := 0
	secondBuy := math.MinInt64
	secondSeld := 0
	for i := 0;i < len(prices);i++{
		firstBuy = int(math.Max(float64(firstBuy),float64(-prices[i])))
		firstSeld = int(math.Max(float64(firstSeld),float64(firstBuy + prices[i])))
		secondBuy = int(math.Max(float64(secondBuy),float64(firstSeld - prices[i])))
		secondSeld = int(math.Max(float64(secondSeld),float64(secondBuy + prices[i])))
	}
	return secondSeld
}
    public int maxProfit4(int[] prices) {
        /**
        對於任意一天考慮四個變量:
        fstBuy: 在該天第一次買入股票可獲得的最大收益 
        fstSell: 在該天第一次賣出股票可獲得的最大收益
        secBuy: 在該天第二次買入股票可獲得的最大收益
        secSell: 在該天第二次賣出股票可獲得的最大收益
        分別對四個變量進行相應的更新, 最後secSell就是最大
        收益值(secSell >= fstSell)
        **/
        int firstBuy = Integer.MIN_VALUE;
        int firstSell = 0;
        int secondBuy = Integer.MIN_VALUE;
        int secondSell = 0;
        for(int i : prices){
            firstBuy = Math.max(firstBuy,-i);
            firstSell = Math.max(firstSell,firstBuy + i);
            secondBuy = Math.max(secondBuy,firstSell - i);
            secondSell = Math.max(secondSell,secondBuy + i);
        }
        return secondSell;
    }

5.買賣股票的最佳時機 IV

在這裏插入圖片描述

這道題目就是上一道的上升了,最多完成k次交易

分析狀態:

dp[i][0]:i次交易下來不持有股票
dp[i][1]:i次交易下來持有股票

分析狀態轉移:

i 次交易下來持有股票,可能是 i 次下來持有股票,或者 i- 1次交易下來不持有股票,但是在第 i 次交易中,買入股票,交易次數+1,達到i次,狀態爲持有股票

 dp[i][1] = Math.max(dp[i][1],dp[i - 1][0] - p)

i 次交易下來不持有股票,可能是 i 次下來不持有股票,或者 i次交易下來持有股票,但是在第 i 次交易中,賣出股票,狀態爲不持有股票

dp[i][0] = Math.max(dp[i][0],dp[i][1] + p)

所以

    public int maxProfit(int k, int[] prices) {
        if(k < 1){
            return 0;
        }
        //當k超過一半時,改用貪心算法
        if(k >= prices.length/2){
            return greedy(prices);
        }
        //dp[i][0]:第i天不持有
        //dp[i][1]:第i天持有
        int[][] dp = new int[k][2];
        for(int i = 0; i < k; ++i) {
            dp[i][1] = Integer.MIN_VALUE;
        }
        for(int p : prices){
            //第一次交易
            dp[0][1] = Math.max(dp[0][1],-p);
            dp[0][0] = Math.max(dp[0][0],dp[0][1] + p);
            //剩下k-1次
            for(int i = 1;i < k;i++){
                dp[i][1] = Math.max(dp[i][1],dp[i - 1][0] - p);
                dp[i][0] = Math.max(dp[i][0],dp[i][1] + p);
            }
        }
        return dp[k - 1][0];
    }

    private int greedy(int[] prices) {
        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;
    }
發佈了261 篇原創文章 · 獲贊 143 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章