动态规划系列:2.基础题目

简单练习


1.斐波拉契数列


斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给你n ,请计算 F(n) 。

方法一:自上而下,递归

//递归,自上而下求解

//递归,自上而下求解
class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        int rValue = fib(n - 1) + fib(n - 2);
        return rValue;
    }
};

方法二:自下而上,动态规划

class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        int *record = new int[n + 1];//0,1,2,3,4,...,n
        record[0] = 0;
        record[1] = 1;
        for (int i = 2; i <= n; i++) {
            record[i] = record[i - 1] + record[i - 2];
        }
        return record[n];
    }
};

2.爬楼梯


假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

分析:

设f(n)表示爬n阶楼梯的方法总数,则:

f(1)=1

f(2)=2

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

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

最后一步有两种状态:跨两阶或者一阶,分别对应f(n-2)和f(n-1)中爬法

代码于斐波拉契数列类似

class Solution {
public:
    int climbStairs(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        if (n == 2) return 2;
        int *record = new int[n + 1];//0,1,2,3,4,...,n
        record[0] = 0;
        record[1] = 1;
        record[2] = 2;
        for (int i = 3; i <= n; i++) {
            record[i] = record[i - 1] + record[i - 2];
        }
        return record[n];

    }
};

3.使用最小花费爬楼梯


给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

分析:在问题2的基础上加入了花费,典型的动态规划问题,符合前面的四个特性

最后一步跨两阶或者一阶,对应两种状态

令f(n)表示爬上n阶楼梯的最小花费

f(n)=min{f(n-1)+cost[n-1], f(n-2)+cost[n-2]}

代码:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        if (n == 0) return 0;
        if (n == 1) return 0;
        if (n == 2) return min(cost[0], cost[1]);
        int *record = new int[n + 1];//0,1,2,3,4,...,n
        record[0] = 0;
        record[1] = 0;
        for (int i = 2; i <= n; i++) {
            record[i] = min(record[i - 1] + cost[i - 1], record[i - 2] + cost[i - 2]);
        }
        return record[n];

    }
};

4.不同路径


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

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

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

分析:这题与前两道爬楼梯很相似,到达终点前只有两个状态

设机器人到达(m,n)有f(m,n)种不同的路径

则f(m,n)=f(m-1,n)+f(m,n-1)

注意边界处理:

左边界和上边界的每个格子,分别等于其上面、左边格子的路径数

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


    }
};

5.不同路径 II


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

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

分析:

在上一题的基础上加入了“不可通行”格子,需要在外面先判断格子能否同行

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        if (m == 0 || n == 0) return 1;
        vector<vector<int>> record(m, vector<int>(n));
        for (int i = 0; i <= m - 1; i++) {
            for (int j = 0; j <= n - 1; j++) {
                if (obstacleGrid[i][j] == 1) record[i][j] = 0;
                else {
                    if (i == 0 && j == 0) record[i][j] = 1;
                    else if (i == 0) record[i][j] = record[i][j - 1];
                    else if (j == 0) record[i][j] = record[i - 1][j];
                    else {
                        record[i][j] = record[i - 1][j] + record[i][j - 1];
                    }
                }
            }
        }
        return record[m - 1][n - 1];
    }
};

6.整数拆分


给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积

与1中的剪绳子差不多

分析:设f(n)表示正整数n在此题下的最大值f(n)

假设将n分成k段,取其中任意一个设为i,则其他所有的子整数的和为n-i,

f(n)=f(i)*f(n-i)

class Solution {
public:
    int integerBreak(int n) {
        if (n == 0 || n == 1)return 0;
        if (n == 2) return 1;
        if (n == 3)return 2;
        vector<int> record(n + 1);
        record[0] = 0;
        record[1] = 1;
        record[2] = 2;
        record[3] = 3;
        for (int i = 4; i <= n; i++) {
            int max_value = 0;
            for (int j = 0; j <= i / 2; j++) {
                max_value = max(max_value, record[j] * record[i - j]);
            }
            record[i] = max_value;
        }
        return record[n];

    }
};

7.不同的二叉搜索树


给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

分析:

n个节点组成:一个根节点,左右子树

以四个节点为例,其根节点从1变至4.

当根节点为1时,左子书0个节点,右子树3个节点,共0+f(3)种子书

当根节点为2时,共f(1)+f(2)

当根节点为3,共f(2)+f(1)

当根节点为4,共f(3)+0

n个节点的二叉搜索树种树:

代码:

class Solution {
public:
    int numTrees(int n) {
        vector<int> record(n + 1);
        if (n == 0)return 0;
        if (n == 1)return 1;
        record[0] = 1;
        record[1] = 1;
        for (int i = 2; i <= n; i++) {//从1个节点到n个节点
            int count_total = 0;//记录i个不同子节点能组成的不同二叉搜索树种数
            for (int j = 1; j <= i; j++) {   //根节点从1变到i
                int count = record[j - 1] * record[i - j];
                count_total += count;
            }
            record[i] = count_total;
        }
        return record[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值