[leetcode初級演算法]動態規劃總結

summer2day發表於2018-11-08

爬樓梯

假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。
示例 1:
輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。

  1. 1 階 + 1 階
  2. 2 階

示例 2:
輸入: 3
輸出: 3
解釋: 有三種方法可以爬到樓頂。

  1. 1 階 + 1 階 + 1 階
  2. 1 階 + 2 階
  3. 2 階 + 1 階

c++程式碼

方法一:遞迴
但是顯示超時了

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        return climbStairs(n-1)+climbStairs(n-2);

    }
};

方法二:迴圈
畢竟還是比遞迴快的

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        int ret=0;
        int pre=2;
        int prepre=1;
        for(int i=3;i<=n;i++)
        {
            ret=pre+prepre;
            prepre=pre;
            pre=ret;
        }
        return ret;
        

    }
};

買賣股票的最佳時機

給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票),設計一個演算法來計算你所能獲取的最大利潤。

注意你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。
示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

c++程式碼

方法一:常規方法
兩次迴圈,找到相差最大的。

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

方法二
遍歷一遍陣列就可以。buy選擇遍歷過的陣列中的最小值,然後每次計算當前值和這個最小值之間的差值做為利潤,然後每次選較大的利潤來更新

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

最大子序和

給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:

如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。

思路:

設n[i]為以第i個元素結尾且和最大的連續子陣列。假設對於元素i,所有以它前面的元素結尾的子陣列的長度都已經求得,那麼以第i個元素結尾且和最大的連續子陣列實際上,要麼是以第i-1個元素結尾且和最大的連續子陣列加上這個元素,要麼是隻包含第i個元素,即n[i]
= max(n[i-1] + nums[i], nums[i])。可以通過判斷n[i-1] + nums[i]是否大於nums[i]來做選擇,而這實際上等價於判斷n[i-1]是否大於0。由於每次運算只需要前一次的結果,因此並不需要像普通的動態規劃那樣保留之前所有的計算結果,只需要保留上一次的即可,因此演算法的時間和空間複雜度都很小

c++程式碼

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sum=nums[0];
        int n=nums[0];
        for(int i=1;i<nums.size();i++) {
            if(n>0)
                n+=nums[i];
            else 
                n=nums[i];
            if(sum<n)
            	sum=n;
        }
        return sum;

    }
};

打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數陣列,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [1,2,3,1]
輸出: 4
解釋: 偷竊 1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3)。
偷竊到的最高金額 = 1 + 3 = 4 。
示例 2:

輸入: [2,7,9,3,1]
輸出: 12
解釋: 偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接著偷竊 5 號房屋 (金額 = 1)。
偷竊到的最高金額 = 2 + 9 + 1 = 12 。

思路:

動態規劃,dp[i]表示到第號房間偷竊到的最大金額。那麼狀態轉移方程怎麼寫呢

我們先拿一個簡單的例子來分析一下,比如說nums為{3, 2, 1, 5},那麼我們來看我們的dp陣列應該是什麼樣的,首先dp[0]=3沒啥疑問,再看dp[1]是多少呢,由於3比2大,所以我們搶第一個房子的3,當前房子的2不搶,所以dp[1]=3,那麼再來看dp[2],由於不能搶相鄰的,所以我們可以用前面的前面的一個的dp值加上當前的房間值,和當前房間的前面一個dp值比較,取較大值當做當前dp值,所以我們可以得到
狀態轉移方程dp[i] = max(num[i] + dp[i - 2], dp[i - 1]),
由此看出我們需要
初始化dp[0]和dp[1],其中dp[0]即為num[0],dp[1]此時應該為max(num[0], num[1])

c++程式碼

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty())
            return 0;
        if(nums.size()==1)
            return nums[0];
        vector<int> dp={nums[0],max(nums[0],nums[1])};
        for(int i=2;i<nums.size();i++)
        {
            dp.push_back(max(dp[i-2]+nums[i],dp[i-1]));
        }
        return dp.back();
    }
};

相關文章