21天掌握动态规划 第一天

简介: 21天掌握动态规划 第一天

首先我觉得得弄明白『动态规划』算法到底是个什么?

上述这张图是摘自截取百度百科对动态规划的定义,我个人觉得主要是有如下几个特点

  • 和分治法类似,将待求解问题划分成若干类似原理子问题,然后从子问题入手最后得到原问题的解
  • 往往是题干出现“最大”“最小”“最好”“最多”等最优化性质的词的时候可以优先考虑动态规划思想(当然也不一定是这样,只是我个人刷题的经验而已)
  • 动态规划很多题往往是需要按照如下两个步骤
  • 确定状态表示的含义,同时确定好基本的初始值
  • 状态转移方程或者不同状态之间存在的依赖关系

以上大致确定了基本的『动态规划』思想,那么接下来就步入正题:

最近刚好看到LeetCode上有一个『21天掌握动态规划的刷题指南

1. 第一天 基础动态规划

1.1 509. 斐波那契数

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1,F(n) = F(n - 1) + F(n - 2),其中 n > 1

给你 n ,请计算 F(n) 。

示例 1:

输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

image.png

C++代码如下:

class Solution {
public:
    int fib(int n) {
        int dp[n + 1];
        if (n <= 1) {
            return n;
        }
        dp[0] = 0, dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

Java代码如下:

class Solution {
    public int fib(int n) {
        int[] dp = new int[n + 1];
        if (n <= 1) {
            return n;
        }
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

优化1:

image.png

C++代码如下:

class Solution {
public:
    int fib(int n) {
        if (n <= 1) {
            return n;
        }
        int a = 0;
        int b = 1;
        int sum = 0;
        for (int i = 2; i <= n; i++) {
            sum = a + b;
            a = b;
            b = sum;
        }
        return b;
    }
};

Java代码如下:

class Solution {
    public int fib(int n) {
        if (n <= 1) {
            return n;
        }
        int a = 0;
        int b = 1;
        int sum = 0;
        for (int i = 2; i <= n; i++) {
            sum = a + b;
            a = b;
            b = sum;
        }
        return b;
    }

优化2

对于这个题的公式而言,很容易联想到高中所学的求通项公式的知识。

查阅资料,找到了一个比较符合当时中学阶段求通项公式的资料

可以看到斐波那契数列通项公式为:

image.png

C++代码:

class Solution {
public:
    int fib(int n) {
        double a = (1 + sqrt(5)) / 2;
        double qa = pow(a,n);
        double b = (1 - sqrt(5)) / 2;
        double qb = pow(b,n);
        double res = ( 1 / sqrt(5) * (qa - qb));
        return (int) res;
    }
};

Java代码:

class Solution {
    public int fib(int n) {
        double a = (1 + Math.sqrt(5)) / 2;
        double qa = Math.pow(a,n);
        double b = (1 - Math.sqrt(5)) / 2;
        double qb = Math.pow(b,n);
        double res = ( (1 / Math.sqrt(5)) * (qa - qb) % 1000000007);
        return (int)res;
    }
}

1.2 70. 爬楼梯

假设你正在爬楼梯。需要 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 阶

image.png

Java代码如下:

class Solution {
    public int climbStairs(int n) {
        if (n <= 2) {
            return n;
        }
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

可以发现和509题斐波那契数很像,只是初始值不一样,那么用滚动数组优化空间复杂度之后如下:

C++代码如下:

class Solution {
public:
    int climbStairs(int n) {
        long a = 1, b = 2;
        long sum = 0;
        if (n <= 2) {
            return n;
        }
        for (int i = 3; i <= n; i++) {
            sum = a + b;
            a = b;
            b = sum;
        }
        return (int)b;
    }
};

Java代码如下:

class Solution {
    public int climbStairs(int n) {
        if (n <= 2) {
            return n;
        }
        long a = 1, b = 2;
        long sum = 0;
        for (int i = 3; i <= n; i++) {
            sum = a + b;
            a = b;
            b = sum;
        }
        return (int) b;
    }
}

同样的,这题也可以给出通项公式的做法,具体的代码可以参考官方题解的。

1.3 746. 使用最小花费爬楼梯

数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。

每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。

请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。

示例 1:

输入:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。

示例 2:

输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出:6
解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。

这个题和上述两题『509.斐波那契数列』、『70.爬楼梯』很相似,不同点在于爬上一个阶梯需要花费对应的体力值,也就是需要多一个对比给定的c o s t costcost数组的值的步骤。

image.png

Java代码如下:

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 0;
        for (int i = 2; i <=n ; i++) {
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[n];
    }
}

滚动数组优化后的代码展示:

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int a = 0, b = 0;
        int sum = 0;
        for (int i = 2; i <=n ; i++) {
            sum = Math.min(a + cost[i - 1], b + cost[i - 2]);
            b = a;
            a = sum;
        }
        return a;
    }
}


相关文章
|
11天前
【题解】—— LeetCode一周小结25
LeetCode每日一道一周小结25
38 8
【题解】—— LeetCode一周小结25
|
2月前
|
机器学习/深度学习 测试技术 C++
【题解】—— LeetCode一周小结14
【题解】—— LeetCode一周小结14
21 2
|
2月前
|
索引
【题解】—— LeetCode一周小结17
【题解】—— LeetCode一周小结17
13 1
|
2月前
|
存储 算法 物联网
【题解】—— LeetCode一周小结3
【题解】—— LeetCode一周小结3
11 0
|
2月前
|
安全 索引
【题解】—— LeetCode一周小结1
【题解】—— LeetCode一周小结1
10 0
|
10月前
蓝桥杯(第一天)
蓝桥杯(第一天)
|
11月前
|
算法
第一天_二分查找【算法入门】
第一天_二分查找【算法入门】
32 0
|
算法 Java
21天掌握动态规划 第二天
21天掌握动态规划 第二天
70 0
21天掌握动态规划 第二天
|
12月前
刷题第一天
我从简单到难,一起走上漫漫刷题路! 爱你们,我会持续在我的博客中更新我每天刷题的内容! 相互交流,爱你们哦!
|
Java
21天掌握动态规划刷题指南 第二天
21天掌握动态规划刷题指南 第二天
67 0

热门文章

最新文章