常見動態規划題目詳解


1.爬樓梯

題目描述:

假設你正在爬樓梯。需要 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 階

實現代碼:

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

2.變態跳台階

題目描述:

一只青蛙一次可以跳上1級台階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的台階總共有多少種跳法。

實現代碼:

class Solution {

public:
    int jumpFloorII(int number) {
        if(number == 0){
            return 0;
        }
        int total = 1;
        for(int i = 1; i < number; i++){
            total *= 2;
        }
        return total;
    }
};
3.n年后牛的數量
題目描述:
假設農場中的母牛每年會產生一頭小母牛,並且永遠不會死。第一年農場中只有一頭成熟的母牛,第二年開始,母牛開始生小母牛,每只小母牛三年之后成熟又可以生小母牛,給定整數N,求N年后母牛的數量。
實現代碼:
class solution{
 public:
   int f(int n){
    if(n < 1){
      return 0;
    }
    if(n == 1|| n== 2||n == 3){
      return n;
    }
    int res = 3;
    int pre = 2;
    int prepre = 1;
    int tmp1=0;
    int tmp2 = 0;
    for(int i = 4;i < n;i++){
      tmp1 = res;
      tmp2 = pre;
      res = pre + prepre;
      pre = tmp1;
      prepre = tmp2;
    }
    return res;
   }
 
} ;
4.矩形覆蓋
題目描述:
我們可以用2*1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
實現代碼:
class Solution {
public:
    int rectCover(int number) {
        if(number <= 2){
            return number;
        }
        int first = 1, second = 2, third = 0;
        for(int i = 3; i <= number; i++){
            third = first + second;
            first = second;
            second = third;
        }
        return third;
    }
};
5.最小路徑和
題目描述:

給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。

說明:每次只能向下或者向右移動一步。

示例:

輸入:
[
  [1,3,1],
[1,5,1],
[4,2,1]
]
輸出: 7
解釋: 因為路徑 1→3→1→1→1 的總和最小。

實現代碼:

class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
vector<vector<int>>dp(m,vector<int>(n));
dp[0][0] = grid[0][0];
for(int i = 1;i < m;i++){
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for(int j = 1;j < n;j++){
dp[0][j] = grid[0][j] + dp[0][j - 1];
}
for(int i = 1;i < m;i++){
for(int j = 1;j < n;j++){
int tmp = dp[i - 1][j] < dp[i][j - 1] ? dp[i - 1][j]:dp[i][j - 1];
dp[i][j] = tmp + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
};

6.機器人到達指定位置的方法數

題目描述:

N個位置,1~N,N大於等於2,開始時機器人在其中的M位置,它可以向左或者右走。如果到了位置1,下一步只能是位置2;如果到了位置N,下一步只能是位置 N-1 。機器人走 K 步,最終來到位置 P 的總方法一共有多少種?

實現代碼:

(1).暴力遞歸

class Solution {
public:
int ways(int N,int M,int K,int P){
if(N < 2 || K < 1|| M < 1|| M > N||P < 1||P > N){
return 0;
}
return walk(N,M,K,N);
}
int walk(int N,int cur,int rest,int P){
if(rest == 0){
return cur == p ? 1 : 0;
}
if(cur == 1){
return walk(N,2,rest - 1,P);
}
if(cur == N){
return walk(N,N - 1,rest - 1,P);
}
return walk(N,cur + 1,rest - 1,P) + walk(N,cur - 1,rest - 1,P);
}
};

(2)動態規划:

class Solution {
public:
int way(int N,int M,int K,int P){
if(N < 2|| K < 1||M < 1||M > N||P < 1|| P > N){
return 0;
}
vector<vector<int>>dp(K + 1,(vector<int>(N + 1)));
dp[0][p] = 1;
for(int i = 1;i <= K;i++){
for(int int j = 1;j < N;j++){
if(j == 1){
dp[i][j] = dp[i - 1][2];
}
else if(j == N){
dp[i][j] = dp[i - 1][N - 1];
}
else{
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
}
}
}
return dp[K][M];
}
};

7.不同路徑

題目描述:

一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為“Start” )。

機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為“Finish”)。

問總共有多少條不同的路徑?

 

例如,上圖是一個7 x 3 的網格。有多少可能的路徑?

說明:m 和 n 的值均不超過 100。

示例 1:

輸入: m = 3, n = 2
輸出: 3
解釋:
從左上角開始,總共有 3 條路徑可以到達右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:

輸入: m = 7, n = 3
輸出: 28

代碼實現:

class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>>dp(m,vector<int>(n));
for(int i = 0;i < m;i++){
dp[i][0] = 1;
}
for(int i = 0;i < n;i++){
dp[0][i] = 1;
}
for(int i = 1;i < m;i++){
for(int j = 1;j < n;j++){
dp[i][j] = dp[i-1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};

8.不同路徑Ⅱ

題目描述:

一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為“Start” )。

機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為“Finish”)。

現在考慮網格中有障礙物。那么從左上角到右下角將會有多少條不同的路徑?

 

網格中的障礙物和空位置分別用 1 和 0 來表示。

說明:m 和 n 的值均不超過 100。

示例 1:

輸入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
輸出: 2
解釋:
3x3 網格的正中間有一個障礙物。
從左上角到右下角一共有 2 條不同的路徑:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

實現代碼:

class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(); //行數
int n = obstacleGrid[0].size();//列數
long p[m][n];

//第一列賦值
int k = 0;
while(k < m && obstacleGrid[k][0] != 1)
p[k++][0] = 1;
//如果遇到了障礙物則它及其后面的值都為0
while(k < m)
p[k++][0] = 0;

//第一行賦值
k = 0;
while(k < n && obstacleGrid[0][k] != 1)
p[0][k++] = 1;
while(k < n)
p[0][k++] = 0;

for(int i = 1; i < m; i++)
for(int j = 1; j < n; j++){
if(obstacleGrid[i][j] == 1) //如果遇到障礙物,則該位置的值為0
p[i][j] = 0;
else
p[i][j] = p[i - 1][j] + p[i][j - 1];
}
return p[m-1][n-1];
}
};

9.買賣股票的最佳時機

題目描述:

給定一個數組,它的第 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。

實現代碼:

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

};

10.打家劫舍

題目描述:

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

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

示例 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 。

實現代碼:

(1).遞歸+記憶化搜索

class Solution {
public:
vector<int>tmpt;
int tryrob(vector<int>& nums,int index){
if(index >= nums.size()){
return 0;
}
if(tmpt[index] != -1){
return tmpt[index];
}
int tmp = -1;
for(int i = index;i < nums.size();i++){
tmp = max(tmp,nums[i] + tryrob(nums,i + 2));
}
tmpt[index] = tmp;
return tmp;
}
int rob(vector<int>& nums) {
tmpt = vector<int>(nums.size(),-1);
return tryrob(nums,0);
}

};

動態規划:

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

11.整數拆分

題目描述:

給定一個正整數 n,將其拆分為至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。

示例 1:

輸入: 2
輸出: 1
解釋: 2 = 1 + 1, 1 × 1 = 1。
示例 2:

輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
說明: 你可以假設 n 不小於 2 且不大於 58。

實現代碼:

遞歸+記憶化搜索

class Solution {
private:
vector<int>tmpt;
int res(int n){
if(n == 1){
return 1;
}
if(tmpt[n] != -1){
return tmpt[n];
}
int tmp = -1;
for(int i = 1;i < n;i++){
tmp = max(max(tmp,i * (n - i)),i * res(n - i));
}
tmpt[n] = tmp;
return tmp;
}

public:
int integerBreak(int n) {
tmpt = vector<int>(n + 1,-1);
return res(n);
}

};

動態規划:

class Solution {

public:
int integerBreak(int n) {
vector<int>tmp(n+1,-1);
tmp[1] = 1;
for(int i = 2;i <= n;i++){
for(int j = 1;j < i;j++){
tmp[i] = max(max(tmp[i],j * (i - j)),j * tmp[i - j]);
}
}
return tmp[n];
}
};

12.三角形最小路徑和

題目描述:

給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。

例如,給定三角形:

[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)。

說明:

如果你可以只使用 O(n) 的額外空間(n 為三角形的總行數)來解決這個問題,那么你的算法會很加分。

實現代碼:

(1).遞歸+記憶化搜索

class Solution {
public:
vector<vector<int>> tmp;
int min_value(vector<vector<int>>& triangle,int i,int j){
if(i == triangle.size() - 1){
return triangle[i][j];
}
if(tmp[i][j] == -1)
tmp[i][j] = triangle[i][j] + min(min_value(triangle,i + 1,j),min_value(triangle,i + 1,j + 1));
return tmp[i][j];
}

int minimumTotal(vector<vector<int>>& triangle) {
int res = 0;
tmp = vector<vector<int>> (1000,vector<int>(1000,-1));
res = min_value(triangle,0,0);
return res;

}

};

(2).動態規划

class Solution {

public:

    int minimumTotal(vector<vector<int>>& triangle) {

        if(triangle.size() == 0){

            return 0;

        }

        vector<int>res;

        int tmp = 0;

        for(int i = triangle.size() - 2;i >= 0;i--){

            for(int j = 0;j < triangle[i].size();j++){

                triangle[i][j] = min(triangle[i + 1][j],triangle[i + 1][j + 1]) + triangle[i][j];

            }

        }

        return triangle[0][0];

    }

};

13.最長回文子串

題目描述:

給定一個字符串 s,找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。

 

示例 1:

 

輸入: "babad"

輸出: "bab"

注意: "aba" 也是一個有效答案。

示例 2:

 

輸入: "cbbd"

輸出: "bb"

實現代碼:

(1).暴力

class Solution {

public:

    string longestPalindrome(string s)

 {

     if(s.empty()) return "";

     if(s.size()==1) return s;

     int start=0,maxlength=1;//記錄最大回文子串的起始位置以及長度

     for(int i=0;i<s.size();i++)

         for(int j=i+1;j<s.size();j++)//從當前位置的下一個開始算

         {

             int temp1,temp2;

             for(temp1=i,temp2=j;temp1<temp2;temp1++,temp2--)

             {

                 if(s[temp1]!=s[temp2])

                     break;

             }

             if(temp1>=temp2 && j-i+1>maxlength)//這里要注意條件為temp1>=temp2,因為如果是偶數個字符,相鄰的兩個經上一步會出現大於的情況

             {

                 maxlength = j-i+1;

                 start=i;

             }

         }

    return s.substr(start,maxlength);//利用string中的substr函數來返回相應的子串,第一個參數是起始位置,第二個參數是字符個數*/

}

};

(2).動態規划

class Solution {

public:

    string longestPalindrome(string s)

 {

     if (s.empty()) return "";

    int len = s.size();

    if (len == 1)return s;

    int longest = 1;

    int start=0;

    vector<vector<int> > dp(len,vector<int>(len));

    for (int i = 0; i < len; i++)

    {

        dp[i][i] = 1;

        if(i<len-1)

        {

            if (s[i] == s[i + 1])

            {

                dp[i][i + 1] = 1;

                start=i;

                longest=2;

            }

        }

    }

    for (int l = 3; l <= len; l++)//子串長度

    {

        for (int i = 0; i+l-1 < len; i++)//枚舉子串的起始點

        {

            int j=l+i-1;//終點

            if (s[i] == s[j] && dp[i+1][j-1]==1)

            {

                dp[i][j] = 1;

                start=i;

                longest = l;

            }

        }

    }

    return s.substr(start,longest);

 }

};

14.最大字序和

題目描述:

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

 

示例:

 

輸入: [-2,1,-3,4,-1,2,1,-5,4],

輸出: 6

解釋: 連續子數組 [4,-1,2,1] 的和最大,為 6。

進階:

 

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

實現代碼:

非動態規划

class Solution {

public:

    int maxSubArray(vector<int>& nums) {

        int sum = 0;

        int max_sum = nums[0];

        for(int i = 0;i < nums.size();i++){

            if(sum <= 0){

                sum = nums[i];

            }

            else

                sum = sum + nums[i];

            if(max_sum < sum){

                max_sum = sum;

            }

        }

        return max_sum;

    }

};

15.最長上升子序列

題目描述:

給定一個無序的整數數組,找到其中最長上升子序列的長度。

 

示例:

 

輸入: [10,9,2,5,3,7,101,18]

輸出: 4 

解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。

說明:

 

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。

你算法的時間復雜度應該為 O(n2) 。

進階: 你能將算法的時間復雜度降低到 O(n log n) 嗎?

實現代碼:

遞歸:

class Solution {

private:

    int dp[100000];

    int LIS(vector<int> &nums,int n){

        if(n == 0){

            dp[n] = 1;

            return dp[n];

        }

        for(int i = n;i>= 0;i--){

            for(int j = i - 1;j >= 0;j--){

                if(nums[i] <= nums[j]){

                    dp[n] = max(LIS(nums,n - 1),;

                }

                if(nums[n] > nums[n - 1]){

                    dp[n] = LIS(nums,n-1);

                }

            }

        }

        return dp[n];

    }

public:

    int lengthOfLIS(vector<int>& nums) {

        if(nums.size() == 0){

            return NULL;

        }

        int n = nums.size() - 1;

        return LIS(nums,n);

    }

};

動態規划:

class Solution {

public:

    int lengthOfLIS(vector<int>& nums) {

        if(nums.size() == 0){

            return NULL;

        }

        int *dp = new int[nums.size()];

        int len = nums.size();

        int res = 0;

        for(int i = 0;i < len;i++){

            dp[i] = 1;

            for(int j = 0;j < i;j++){

                if(nums[i] > nums[j]){

                    dp[i] = max(dp[j] + 1,dp[i]);

                }

            }

            res = max(dp[i],res);

        }

        return res;

    }

};

16.最長公共子序列

題目描述:

給定兩個字符串str1,str2返回兩個字符串的最長公共子序列

實現代碼:

class Solution {

public:

    int maxlength(string str1,string str2){

        int m = str1.length();

        int n = str2.length();

        vector<vector<int>>dp(m,vector<int>(n));

        dp[0][0] = str1[0] == str2[0]?1:0;

        for(int i = 0;i < m;i++){

            dp[i][0] = max(dp[i - 1][0],str1[i] == str2[0]?1:0);

        }

        for(int j = 0;j < n;j++){

            dp[0][j] = max(dp[0][j - 1],str1[0] == str[j] ? 1: 0);

        }

        for(int i = 1; i < m;i++){

            for(int j = 1;j < n;j++){

                dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);

                if(str1[i] == str2[j]){

                    dp[i][j] = max(dp[i][j],1 + dp[i - 1][j - 1]);

                }

                

            }

        }

        return dp[m - 1][n - 1];

    }

};

17.最長公共字串

題目描述:

給定兩個字符串str1,str2,返回兩個字符串的最長公共字串

實現代碼:

class Solution {

public:

    int maxlength(string str1,string str2){

        int m = str1.length();

        int n = str2.length();

        vector<vector<int>>dp(m,vector<int>(n));

        for(int i = 0;i < m;i++){

            if(str1[i] == str2[0]){

                dp[i][0] = 1;

            }

        }

        for(int j = 0;j < n;j++){

            if(str2[j] == str1[0]){

                dp[0][j] = 1;

            }

        }

        for(int i = 1;i < m;i++){

            for(int j = 1;j < n;j++){

                if(str1[i] == str2[j]){

                    dp[i][j] = dp[i - 1][j - 1] + 1;

                }

            }

        }

        return dp[m - 1][n - 1];

    }

};

18.連續子數組和

題目描述:

給定一個包含非負數的數組和一個目標整數 k,編寫一個函數來判斷該數組是否含有連續的子數組,其大小至少為 2,總和為 k 的倍數,即總和為 n*k,其中 n 也是一個整數。

 

示例 1:

 

輸入: [23,2,4,6,7], k = 6

輸出: True

解釋: [2,4] 是一個大小為 2 的子數組,並且和為 6。

示例 2:

 

輸入: [23,2,6,4,7], k = 6

輸出: True

解釋: [23,2,6,4,7]是大小為 5 的子數組,並且和為 42。

說明:

 

數組的長度不會超過10,000。

你可以認為所有數字總和在 32 位有符號整數范圍內。

實現代碼:

class Solution {

public:

    bool checkSubarraySum(vector<int>& nums, int k) {

        int m = nums.size();

        if(m < 2){

            return false;

        }

        int sum[m];

        sum[0] = nums[0];

        for(int i = 1;i < m;i++){

            sum[i] = sum[i - 1] + nums[i];

        }

        for(int i = 0;i < m;i++){

            for(int j = i + 1;j < m;j++){

                int res = sum[j] - sum[i] + nums[i];

                if(res == k || (k != 0 && res % k == 0)){

                    return true;

                }

            }

        }

        return false;

    }

};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM