LeetCode動態規劃題目總結(持續更新中)

Climbing Stairs

原文:https://blog.csdn.net/xpy870663266/article/details/104665131

70. Climbing Stairs - Easy

題目:https://leetcode.com/problems/climbing-stairs/submissions/

You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

首先將問題形式化,設有nn級臺階,位置分別標號[1,2,...,n][1,2,...,n],我們一開始處於位置00,最後要到達位置nn.

dp[i]表示到達位置ii的方法數.

狀態轉移方程

到達位置i有兩種情況(狀態轉移來源):從位置i-1走一步臺階到達i;從位置i-2走兩步臺階到達位置i,故狀態轉移方程:dp[i]=dp[i-1]+dp[i-2]

初始狀態

從位置0出發,到達位置1只有1種可能,到達位置2有2種可能.

代碼如下

未經過空間優化:

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)return 1;
        if(n==2)return 2;
        vector<int> dp(n+1,0);
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

經過空間優化:

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)return 1;
        if(n==2)return 2;
        int f_n_1=1;
        int f_n=2;
        for(int i=3;i<=n;i++){
            int tmp=f_n+f_n_1;
            f_n_1=f_n;
            f_n=tmp;
        }
        return f_n;
    }
};

746. Min Cost Climbing Stairs - Easy

題目:https://leetcode.com/problems/min-cost-climbing-stairs/

On a staircase, the i-th step has some non-negative cost cost[i]
assigned (0 indexed).

Once you pay the cost, you can either climb one or two steps. You need
to find minimum cost to reach the top of the floor, and you can either
start from the step with index 0, or the step with index 1.

思路:todo

代碼:


class Solution {
public:
	// 未經過空間優化
    // int minCostClimbingStairs(vector<int>& cost) {
    //     vector<int> dp(cost.size()+1);
    //     if(cost.size()<=2)return 0;
    //     dp[0]=0;
    //     dp[1]=0;
    //     for(int i=2;i<dp.size();i++){
    //         dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
    //     }
    //     return dp.back();
    // }
    int minCostClimbingStairs(vector<int>& cost) {
        if(cost.size()<=2)return 0;
        int dpi_2=0;
        int dpi_1=0;
        int dpi;
        for(int i=2;i<cost.size()+1;i++){
            dpi=min(dpi_1+cost[i-1],dpi_2+cost[i-2]);
            dpi_2=dpi_1;
            dpi_1=dpi;
        }
        return dpi;
    }
};

Subarray, Substring and Subsequence

53. Maximum Subarray - Easy

題目:https://leetcode.com/problems/maximum-subarray/

Given an integer array nums, find the contiguous subarray (containing
at least one number) which has the largest sum and return its sum.

dp[i]表示以nums[i]結尾的子數組的元素求和最大值,則有dp[i]=max(nums[i],dp[i-1]+nums[i]),由於dp[i]只依賴前一個狀態dp[i-1],故可以優化爲只使用一個變量來保存dp[i].

經過空間優化的代碼如下

class Solution {
public:
    int maxSubArray(vector<int>& n) {
        if(n.size()==0)return 0;
        int res=n[0];  //記錄dp[0]到dp[i]的最大值,作爲最終結果
        int smax=n[0]; //記錄dp[i]的值
        for(int i=1;i<n.size();i++){
            smax=max(n[i],n[i]+smax);
            if(smax>res)res=smax;
        }
        return res;
    }
};

152. Maximum Product Subarray - Medium

題目:https://leetcode.com/problems/maximum-product-subarray/

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

dp[i]表示以n[i]結尾的子數組的元素求積值,本題與上一題LeetCode53(簡寫爲LC53,下同)的區別在於,dp[i]無法僅由n[1]dp[i-1]轉移得到。例如對於數組n:[-2, 3, -4]dp[1]=3max(n[2],dp[1])=3,而實際上dp[2]=n[0]*n[1]*n[2]=24≠max(n[2],dp[1])。實際上本題需要構造兩個dp數組,一個是前面所說的,另一個則是表示以n[i]結尾的子數組的元素求積值。這種構造兩個dp數組的問題在後面的LC309、LC714中也有用到。

設兩個dp數組分別爲maxdp[i]mindp[i],分別表示以n[i]結尾的子數組的元素求積最大/小值。則maxdp[i]的轉移方程爲:

如果n[i]大於等於0:
	maxdp[i]=max(n[i], n[i]*maxdp[i-1])
否則n[i]小於0:
	maxdp[i]=max(n[i], n[i]*mindp[i-1])

mindp[i]的轉移方程爲:

如果n[i]大於等於0:
	mindp=min(n[i], n[i]*mindp[i-1])
否則:
	mindp=min(n[i], n[i]*maxdp[i-1])

另外,由於兩個dp數組的第i個狀態都只依賴第i-1個狀態,所以可以在空間上優化,分別用smaxsmin表示maxdp[i]mindp[i]

代碼如下

class Solution {
public:
    int maxProduct(vector<int>& n) {
        if(n.size()==0)return 0;
        int res=n[0];
        int smin=res,smax=res; //min value and max value for current state
        for(int i=1;i<n.size();i++){
            if(n[i]>=0){
                smax=max(n[i],n[i]*smax);
                smin=min(n[i],n[i]*smin);
            }else{
            	//先用tmp保存samx(即maxdp[i-1])是因爲:
            	//smax在下一行將被賦值改變,表示得到maxdp[i],但下下行的求smin,即mindp[i]時又要用到maxdp[i-1]
                int tmp=smax; 
                smax=max(n[i],n[i]*smin); 
                smin=min(n[i],n[i]*tmp);
            }
            if(smax>res)res=smax;
        }
        return res;
    }
};

Buy and Sell Stock

原文:https://blog.csdn.net/xpy870663266/article/details/104665131

121. Best Time to Buy and Sell Stock - Easy

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

Say you have an array for which the ith element is the price of a
given stock on day i.

If you were only permitted to complete at most one transaction (i.e.,
buy one and sell one share of the stock), design an algorithm to find
the maximum profit.

Note that you cannot sell a stock before you buy one.

對於每一天來說,在這天賣出能得到最高profit,對應的是在之前的幾天中最低價的那天買入。所以只需要遍歷一次,並用變量維護前面幾天的最低價是多少,對於每一天用其價格減去前幾天的最低價得到當天能得到的最高profit,如果這個profit是見過的最大profit(也用一個變量來保存)就更新。

代碼如下

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int min_value=INT_MAX;
        int max_profit=0;
        for(auto p:prices){
            if(p-min_value>max_profit){
                max_profit=p-min_value;
            }
            if(p<min_value){
                min_value=p;
            }
        }
        return max_profit;
    }
};

309. Best Time to Buy and Sell Stock with Cooldown - Medium

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

Say you have an array for which the ith element is the price of a
given stock on day i.

Design an algorithm to find the maximum profit. You may complete as
many transactions as you like (ie, buy one and sell one share of the
stock multiple times) with the following restrictions:

  • You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
  • After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

參考:這篇帖子

三種操作:

  1. buy買入股票,操作之後利益需要減去prices[i];
  2. sell賣出股票,操作之後利益需要加上prices[i];
  3. rest休息,對獲得的利益沒有影響。

設buy[i]代表第i天buy並在剩下的天數都採取rest操作,這種情況下能得到的最大利益。

設sell[i]代表第i天賣出股票,並在剩下的天數中都採取rest操作,這種情況下能夠得到的最大利益。

容易得知,最大利益的操作序列一定是以一個sell和k個(k≥0)rest操作結尾的,不會以一個buy和k個(k≥0)rest操作結尾。因此,sell數組的最大值就是能得到最大利益。

狀態轉移方程

方程一:sell[i]=max(buy[i-1]+prices[i], sell[i-1]-prices[i-1]+prices[i])

理由:第i天賣出股票則在之前必須先買入股票,上次buy的時間分兩種情況:

(1)在第i-1天買入;(2)在第k天(k ≤ i-2)天買入。

第(1)種情況可以用buy[i-1]+prices[i]來表示;第(2)種情況可以利用sell[i-1]來得出,這是因爲sell[i-1]就包括了在第k(k ≤ i-2)天買入的所有情況。所以第(2)種情況可以用sell[i-1]-prices[i-1]+prices[i]來得到,表示“將在第i-1天賣出修改爲在第i天賣出

方程二:buy[i]=max(sell[i-2]-prices[i], buy[i-1]+prices[i-1]-prices[i])

類似方程一的分析思路。根據規則,第i天買入則第i-1天必須是rest,所以上次sell的時間只能有兩種情況:

(1)在i-2天賣出;(2)在第k天(k≤i-3)天賣出

兩種情況分別對應sell[i-2]-prices[i]buy[i-1]+prices[i-1]-prices[i]buy[i-1]包含了所有在第k天賣出的情況,其中k≤i-3;注意對buy[i-1]來說,第i-2天肯定在rest)

代碼如下

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        
        //if len==1, just REST and get max profit 0
        if(len<=1)return 0;
        //REST for 2 days Or buy in day0 sell in day1
        if(len==2)return max(0,prices[1]-prices[0]);
        
        // if at least 3 days
        vector<int> buy(len,0);
        vector<int> sell(len,0);
        
        // 初始狀態的代碼
        //sell[0]其實不存在,但是buy[2]肯定是等於-prices[2],爲了遞歸方程可以算出buy[2],需要sell[0]有個合理的值
        sell[0]=0;
        buy[0]=-prices[0];  //第0天買入,虧損prices[0],注意負號
        sell[1]=buy[0]+prices[1];  //要想在第1天賣出,只可能在第0天買入
        buy[1]=-prices[1];  //要想在第1天買入,第0天只能rest(第0天buy了第1天不能buy)
        
        int max_profit=max(sell[1],0); //0 means rest for 2 days

        for(int i=2;i<len;i++){
            sell[i]=max(buy[i-1]+prices[i],sell[i-1]-prices[i-1]+prices[i]);
            buy[i]=max(sell[i-2]-prices[i],buy[i-1]+prices[i-1]-prices[i]);
            if(sell[i]>max_profit)max_profit=sell[i];
        }
        return max_profit;
    }
};

714. Best Time to Buy and Sell Stock with Transaction Fee - Medium

題目:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

Your are given an array of integers prices, for which the i-th element
is the price of a given stock on day i; and a non-negative integer fee
representing a transaction fee.

You may complete as many transactions as you like, but you need to pay
the transaction fee for each transaction. You may not buy more than 1
share of a stock at a time (ie. you must sell the stock share before
you buy again.)

Return the maximum profit you can make.

和前面的第309題思路一樣,代碼也幾乎完全一樣,區別僅在於第i天賣出時還要虧損一筆fee,所以需要修改第一條轉移方程,由

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

改爲:

sell[i]=max(buy[i-1]+prices[i]-fee, sell[i-1]-prices[i-1]+prices[i])

另外,初始化sell[1]時也要記得減去fee.

代碼如下

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int len=prices.size();
        //處理特殊情況,即len≤2的情況

        if(len<=1)return 0;//空序列最大利益是0,只有一個時最大利益就是rest

        //0代表兩天都是rest,prices[1]-prices[0]代表第0天買入,第1天賣出
        if(len==2)return max(0,prices[1]-prices[0]);
        
        // 如果len至少爲3
        vector<int> buy(len,0);
        vector<int> sell(len,0);

        //sell[0]其實不存在,但是buy[2]肯定是等於-prices[2],爲了遞歸方程可以算出buy[2],需要sell[0]有個合理的值
        sell[0]=0;
        buy[0]=-prices[0];
        sell[1]=buy[0]+prices[1]-fee;
        buy[1]=-prices[1];

        int max_profit=max(sell[1],0); //0代表前兩天全爲rest,不買也不賣

        for(int i=2;i<len;i++){
            sell[i]=max(buy[i-1]+prices[i]-fee,sell[i-1]-prices[i-1]+prices[i]);
            buy[i]=max(sell[i-1]-prices[i],buy[i-1]+prices[i-1]-prices[i]);
            if(sell[i]>max_profit)max_profit=sell[i];
        }

        return max_profit;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章