动态规划-基础题目

题目链接:509. 斐波那契数 - 力扣(Leetcode)

// 动规五部曲
// 1.确定dp数组以及下标的含义
// 2.确定递推公式
// 3.dp数组如何初始化
// 4.确定遍历顺序
// 5.举例推到dp数组
// class Solution {
// public:
//     int fib(int n) {
//         if(n<=1)    return n;
//         vector<int> dp(n+1);
//         dp[0]=0;
//         dp[1]=1;
//         for(int i=2;i<=n;i++){
//             dp[i]=dp[i-1]+dp[i-2];
//         }
//         return dp[n];
//     }
// };

// 优化空间复杂度
class Solution {
public:
    int fib(int n) {
        if(n<2) return n;
        int dp[2];
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            int tmp=dp[0]+dp[1];
            dp[0]=dp[1];
            dp[1]=tmp;
        }
        return dp[1];
    }
};

// 递归
// class Solution {
// public:
//     int fib(int n) {
//         if(n==0){
//             return 0;
//         }
//         else if(n==1){
//             return 1;
//         }
//         else{
//             return fib(n-1)+fib(n-2);
//         }
//     }
// };

题目链接:70. 爬楼梯 - 力扣(Leetcode)

class Solution {
public:
    int climbStairs(int n) {
        // 先推理爬上一阶有1种方法,爬上二阶有2种方法。
        // 那么爬上三阶,就是从一阶一步爬两个台阶上三阶或从二阶一步一个台阶上三阶,有1+2=3种方法
        // 所以
        // dp[i]表示爬到n个台阶有多少种方法
        // 爬上i-1阶有dp[i-1]种方法,再一步一个台阶就是dp[i];爬上i-2阶有dp[i-2]种方法,再一步两个台阶就是dp[i]
        // dp[i]=dp[i-1]+dp[i-2]
        vector<int> dp(n+1);
        for(int i=1;i<=n;i++){
            if(i<3) dp[i]=i;
            else{
                dp[i]=dp[i-1]+dp[i-2];
            }
        }
        return dp[n];
    }
};

// 优化空间复杂度
// 因为只需要维护两个数值即可,不需要记录整个序列
// class Solution {
// public:
//     int climbStairs(int n) {
//         if(n<=1)    return n;
//         int dp[2];
//         // 这里代码随想录错误,应该是dp[1]和dp[2],从3开始
//         dp[0]=1;//存放dp[i-2]
//         dp[1]=2;//存放dp[i-1]
//         for(int i=3;i<=n;i++){
//             int sum=dp[0]+dp[1];
//             dp[0]=dp[1];
//             dp[1]=sum;
//         }
//         return dp[1];//返回dp[i]
//     }
// };

题目链接:746. 使用最小花费爬楼梯 - 力扣(Leetcode)

// class Solution {
// public:
//     int minCostClimbingStairs(vector<int>& cost) {
//         // dp[i]为爬到第i个台阶的最低费用
//         // dp[0]=cost[0];dp[1]=cost[1]
//         // dp[2]=min(dp[0]+cost[2],dp[1]+cost[2])
//         // 以此类推,dp[i]=min(dp[i-2]+cost[i],dp[i-1]+cost[i])

//         // 上面属于是理解错误了,题目意思是爬到cost.size()而不是cost.size()-1
//         // dp[0]=0,dp[1]=0;再从第0阶/第1阶往上跳的时候要支付cost[0]/cost[1]的费用
//         // dp[i]=min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1])
//         // 这里dp[]要比cost多一个,返回的也是dp[]最后一个
//         vector<int> dp(cost.size()+1);
//         for(int i=0;i<cost.size()+1;i++){
//             if(i<2) dp[i]=0;
//             else{
//                 dp[i]=min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);
//             }
//         }
//         return dp[cost.size()];
//     }
// };

// 优化空间复杂度
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp[2];
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<cost.size()+1;i++){
            int tmp=min(dp[0]+cost[i-2],dp[1]+cost[i-1]);
            dp[0]=dp[1];
            dp[1]=tmp;
        }
        return dp[1];
    }
};

题目链接:62. 不同路径 - 力扣(Leetcode)

// 动态规划
// 通过分析具体案例发现dp[i][0]=dp[0][j]=1
// 继续分析发现dp[i][j]=dp[i][j-1]+dp[i-1][j];
// 时间复杂度:O(m × n);空间复杂度:O(n)
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m,vector<int>(n));
        // 这里在赋值dp数组时,要注意要先将dp[i][0]=dp[0][j]=1赋值才可以继续计算,因为其他位置都依赖于他们,否则是不能计算的
        for(int i=0;i<m;i++)   dp[i][0]=1;
        for(int j=0;j<n;j++)   dp[0][j]=1;
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=dp[i][j-1]+dp[i-1][j];
            }
        }
        return dp[m-1][n-1];
    }
};

// 要从[0,0]走到[m,n]一定要有m-1步向下,n-1步向右,一共要走m+n-1步
// 那么可以转换为排列组合问题,什么时候向右走什么时候向下走
// 组合问题不应该用回溯吗?
// class Solution {
// public:
//     int uniquePaths(int m, int n) {
//         long long numerator=1;
//         int denominator=m-1;
//         int count=m-1;
//         int t=m+n-2;
//         // 说实话,根本看不懂
//         while(count--){
//             numerator*=(t--);
//             while(denominator!=0&&numerator%denominator==0){
//                 numerator/=denominator;
//                 denominator--;
//             }
//         }
//         return numerator;
//     }
// };

题目链接:63. 不同路径 II - 力扣(Leetcode)

// 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度;空间复杂度:O(n × m)
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        // 初始化dp数组为全0
        // 初始化dp[i][0]时,当前面没有障碍物则dp[i][0]=1,一旦前面有障碍物则dp[i][0]=0;dp[0][j]同理
        vector<vector<int>> dp(m,vector<int>(n,0));
        for(int i=0;i<m&&obstacleGrid[i][0]==0;i++) dp[i][0]=1;
        for(int j=0;j<n&&obstacleGrid[0][j]==0;j++) dp[0][j]=1;
        // dp[i][j]仍与上一题相同,只不过遇到障碍物时dp[i][j]=0
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(obstacleGrid[i][j]==0)   dp[i][j]=dp[i][j-1]+dp[i-1][j];
                else    continue;
            }
        }
        return dp[m-1][n-1];
    }
};

// 空间复杂度优化
// 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度;空间复杂度:O(m)
// class Solution {
// public:
//     int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
//         if(obstacleGrid[0][0]==1)   return 0;
//         // 按行存储dp
//         vector<int> dp(obstacleGrid[0].size());
//         for(int j=0;j<dp.size();j++){
//             if(obstacleGrid[0][j]==1)   dp[j]=0;
//             else if(j==0)   dp[j]=1;
//             else    dp[j]=dp[j-1];
//         }
//         for(int i=1;i<obstacleGrid.size();i++){
//             for(int j=0;j<dp.size();j++){
//                 if(obstacleGrid[i][j]==1)   dp[j]=0;
//                 // 这里dp[j]存储的是dp[i-1][j],dp[j-1]存储的是dp[i][j-1]
//                 else if(j!=0)   dp[j]=dp[j]+dp[j-1];
//             }
//         }
//         return dp.back();
//     }
// };

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值