[Leetcode 动态规划]

 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 步
 
解:

利用枚举抽象出一般表达式

当n=0时,f(n)=1

n=1,f(n)=1

n=2,f(n)=2

n=3,fn=3

n=4,fn=5

f(2)=f(1)+f(0)

f(3)=f(2)+f(1)

f(4)=f(3)+f(2)

.......

f(n)=f(n-2)+f(n-1)

递归:

class Solution {
public:
    int climbStairs(int n) {
        //f(n)=f(n-2)+f(n-1)
        if (n == 0) {
            return 0;
        }
        
        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<2) return 1;
        int a1=1,a2=1;
        for(int i=2;i<n;i++){
            swap(a2,a1);
            a2=a1+a2;
        }
        return a1+a2;
    }
};

2.不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m 和 的值均不超过 100。

示例 1:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

示例 2:

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

思路:

这是一道典型的动态规划问题,使用一个二维数组ans记忆到达每一点可行的走法总数。首先将左边界点和上边界点初始化为1,因为机器人起始与(0,0),左边界点和上边界点的走法只有1种。接下来的每一点(x,y),可以由(x-1,y)向右走或是(x,y-1)向下走来到达,因此在(x,y)这一点可到达的方法有ans[x-1][y]+ans[x][y-1]种,到达终点的方法则是ans最后一个点的数据。

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

3.跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

思路:这步能跳多远取决于两个因素:当前脚力C与之前的剩余步数L。当前脚力C为非负整数,剩余步数L与前一步的脚力和剩余步数有关,其关系为。

                    L[i]=max(C[i],L[i])-1;

如果之前剩余步数和当前的脚力都不足以买过前面这个坎,就说明永远无法走到最后一步。

因此当前脚力时直接可以知道的,因此剩余脚力是一个从第一步开始维护的动态规划量;

class Solution {
public:
    bool canJump(vector<int>& nums) 
    {
        if(nums.size()<=1)
            return true;
        int L=0;
        int C=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            L=max(L,nums[i-1])-1;
            if(L<0)
                return false;
        }
        
        return L>=0;
        
    }
};

4. 数组串查找

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

思路 : 我们维护一个一位数组dp,其中dp[i]表示达到i位置时剩余的步数,那么难点就是推导状态转移方程啦。我们想啊,到达当前位置的剩余步数跟什么有关呢,其实是跟上一个位置的剩余步数和上一个位置的跳力有关,这里的跳力就是原数组中每个位置的数字,因为其代表了以当前位置为起点能到达的最远位置。所以当前位置的剩余步数(dp值)和当前位置的跳力中的较大那个数决定了当前能到的最远距离,而下一个位置的剩余步数(dp值)就等于当前的这个较大值减去1,因为需要花一个跳力到达下一个位置,所以我们就有状态转移方程了:dp[i] = max(dp[i - 1], nums[i - 1]) - 1,如果当某一个时刻dp数组的值为负了,说明无法抵达当前位置,则直接返回false,最后我们判断dp数组最后一位是否为非负数即可知道是否能抵达该位置,代码如下:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(), 1);
        int res = 0;
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                if (nums[i] > nums[j]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            res = max(res, dp[i]);
        }
        return res;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_34570910/article/details/81013447