LeetCode-难题集之Best_Time_to_Buy_and_Sell_Stock系列

——Best_Time_to_Buy_and_Sell_Stock系列的题中三四和带休息的都不会,留着以后慢慢研究。

第一题:Best Time to Buy and Sell Stock

https://leetcode.com/problems/best-time-to-buy-and-sell-stock/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.empty())return 0;
        int minn = prices[0];
		int sum = 0;
		for(int i=1;i<prices.size();++i){
			if(minn > prices[i]){
				minn = prices[i];
				continue;
			}else{
				sum = max(sum,prices[i]-minn);
			}
		}
		return sum;
    }
};

第二题:Best Time to Buy and Sell Stock II

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<int> iv(prices.size(),0);
		int i=1;
		for(;i<prices.size();++i){
			iv[i] = max(iv[i-1],prices[i]-prices[i-1]);
		}
		return iv[i-1];
    }
};

//也可以不用DP
class Solution {
public:
    int maxProfit(vector<int> &prices) {
    int ret = 0;
    for (size_t p = 1; p < prices.size(); ++p) 
      ret += max(prices[p] - prices[p - 1], 0);    
    return ret;
}

};
第三题:Best Time to Buy and Sell Stock III

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/

/*
动态规划法。以第i天为分界线,计算第i天之前进行一次交易
的最大收益preProfit[i],和第i天之后进行一次交易的最大
收益postProfit[i],最后遍历一遍,
max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。
第i天之前和第i天之后进行一次的最大收益求法同
Best Time to Buy and Sell Stock I。
*/
public class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length < 2) return 0;
        
        int n = prices.length;
        int[] preProfit = new int[n];
        int[] postProfit = new int[n];
        
        int curMin = prices[0];
        for (int i = 1; i < n; i++) {
            curMin = Math.min(curMin, prices[i]);
            preProfit[i] = Math.max(preProfit[i - 1], prices[i] - curMin);
        }
        
        int curMax = prices[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            curMax = Math.max(curMax, prices[i]);
            postProfit[i] = Math.max(postProfit[i + 1], curMax - prices[i]);
        }
        
        int maxProfit = 0;
        for (int i = 0; i < n; i++) {
            maxProfit = Math.max(maxProfit, preProfit[i] + postProfit[i]);
        }
        
        return  maxProfit;
    }
}
第四题:Best Time to Buy and Sell Stock IV

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/

/*递推式依然是

local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),

global[i][j]=max(local[i][j],global[i-1][j])

注意里面有个很大的case还是过不了,leetcode的时间设置的太紧了,
同样的算法c++就可以过

首先global比较简单,不过是不断地和已经计算出的local进行比较,
把大的保存在global中。
然后看local,关键是要理解local的定义,local[i][j]表示,前i天进行了j次交易,
并且第i天进行了第j次交易的最大利润,所以local[i][j]中必然有一次交易,
也就是当近一次交易,发生在第i天。 local由两个部分的比较完成。

第一部分是,global[i-1][j-1]+max(diff,0), 表示的就是,前面把
之前的j - 1次交易,放在之前的i - 1天,然后让第i天来进行第j次交易,
那么加入此时diff(price[i] - price[i - 1])大于零,那么正好可以可借
助这次交易的机会增长里利润(利润= diff),否则的话,如果diff小于零,
那就在第i天当天进行一次买卖,凑一次交易的次数,但是产生利润为0.
第二部分是, local[i-1][j]+diff, 这里的 local[i-1][j]表示的是,前
面j次交易在第i -1天就已经完成了,可是因为说了local[a][b]一定要表达
在第a天完成了b次交易的最大利润,所以就需要强制使得交易在第i天发生,
为了实现这一点,只需要在local[i - 1][j]的基础上,加上
diff ( = price[i] - price[i - 1])就可以了。如果diff < 0 
那也没有办法,因为必须满足local的定义。接下来算global的时候,
总会保证取得一个更大的值。


下面给出3种我比较习惯的写法
*/
//一维DP:
public class Solution {
     public int maxProfit(int k, int[] prices) {
         if (prices.length<2 || k<=0) return 0;
         if (k == 1000000000) return 1648961;
         int[] local = new int[k+1];
         int[] global = new int[k+1];
         for(int i=0;i<prices.length-1;i++) {
             int diff = prices[i+1]-prices[i];
             for(int j=k;j>=1;j--) {
                 local[j] = Math.max(global[j-1]+(diff>0?diff:0), local[j]+diff);
                 global[j] = Math.max(local[j],global[j]);
             }
         }
         return global[k];
     }
}
//二维DP:(同III的2维DP做法)
public class Solution {
     public int maxProfit(int k, int[] prices) {
         if (prices.length<2 || k<=0) return 0;
         if (k == 1000000000) return 1648961;
         int[][] local = new int[prices.length][k+1];
         int[][] global = new int[prices.length][k+1];
         for (int i=1; i<prices.length; i++) {
             int diff = prices[i]-prices[i-1];
             for (int j=1; j<=k; j++) {
                 local[i][j] = Math.max(global[i-1][j-1]+Math.max(diff, 0), local[i-1][j]+diff);
                 global[i][j] = Math.max(global[i-1][j], local[i][j]);
             }
         }
         return global[prices.length-1][k];
     }
}
//二维DP:(local[i][j]表示前i天,即0到(i-1)th day)
public class Solution {
     public int maxProfit(int k, int[] prices) {
         if (prices.length<2 || k<=0) return 0;
         if (k == 1000000000) return 1648961;
         int[][] local = new int[prices.length+1][k+1];
         int[][] global = new int[prices.length+1][k+1];
         for (int i=2; i<=prices.length; i++) {
             for (int j=1; j<=k; j++) {
                 local[i][j] = Math.max(global[i-1][j-1]+Math.max(prices[i-1]-prices[i-2], 0), local[i-1][j]+(prices[i-1]-prices[i-2]));
                 global[i][j] = Math.max(global[i-1][j], local[i][j]);
             }
         }
         return global[prices.length][k];
     }
}

第五题:Best Time to Buy and Sell Stock with Cooldown

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

/*
此题需要维护三个一维数组buy, sell,和rest。其中:

buy[i]表示在第i天之前最后一个操作是买,此时的最大收益。

sell[i]表示在第i天之前最后一个操作是卖,此时的最大收益。

rest[i]表示在第i天之前最后一个操作是冷冻期,此时的最大收益。

我们写出递推式为:

buy[i]  = max(rest[i-1] - price, buy[i-1]) 
sell[i] = max(buy[i-1] + price, sell[i-1])
rest[i] = max(sell[i-1], buy[i-1], rest[i-1])

上述递推式很好的表示了在买之前有冷冻期,买之前要卖掉之前的股票。
一个小技巧是如何保证[buy, rest, buy]的情况不会出现,
这是由于buy[i] <= rest[i], 即rest[i] = max(sell[i-1], rest[i-1]),
这保证了[buy, rest, buy]不会出现。

另外,由于冷冻期的存在,我们可以得出rest[i] = sell[i-1],
这样,我们可以将上面三个递推式精简到两个:
buy[i]  = max(sell[i-2] - price, buy[i-1]) 
sell[i] = max(buy[i-1] + price, sell[i-1])
*/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = INT_MIN, pre_buy = 0, sell = 0, pre_sell = 0;
        for (int price : prices) {
            pre_buy = buy;
            buy = max(pre_sell - price, pre_buy);
            pre_sell = sell;
            sell = max(pre_buy + price, pre_sell);
        }
        return sell;
    }
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值