8. 动态规划基础

一. 什么是动态规划?

  • 通过使用递归、记忆化搜索、动态规划 三种方法 解 斐波那契数列问题, 来说明

递归

  • 从上而下
  • 但存在大量重复计算
  • 举例: f(10)=f(9)+f(8) 与 f(9)=f(8)+f(7) 这里f(8)就被重复计算了
#include <iostream>
#include <ctime>

using namespace std;

int num = 0;

// 递归求斐波那契数列
int fib( int n ){

    num ++;

    if( n == 0 )
        return 0;

    if( n == 1 )
        return 1;

    return fib(n-1) + fib(n-2);
}

记忆化搜索

  • 在递归的基础上优化
  • 将计算过的f(n)都保存起来, 避免重复计算
#include <iostream>
#include <ctime>
#include <vector>

using namespace std;

vector<int> memo;  // 用来保存 计算过的数值
int num = 0;

// 记忆化搜索
int fib(int n){

    num ++;

    if(n == 0)
        return 0;

    if(n == 1)
        return 1;

    if(memo[n] == -1)
        memo[n] = fib(n - 1) + fib(n - 2);

    return memo[n];
}

动态规划

  • 递归是从上而下的, 动态规划则是自下而上的
#include <iostream>
#include <ctime>
#include <vector>
using namespace std;

// 动态规划
int fib( int n ){

    vector<int> memo(n + 1, -1);

    memo[0] = 0;
    memo[1] = 1;
    for(int i = 2 ; i <= n ; i ++)
        memo[i] = memo[i - 1] + memo[i - 2];

    return memo[n];
}

  • 一般来说, 记忆化搜索也属于动态规划范畴

二.第一个动态规划问题 Climbing Stairs

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

递归解决

  • 直接使用 依然会存在 重复计算的问题
  • 配合记忆化搜索 来解决问题
#include <iostream>
#include <vector>

using namespace std;

/// 70. Climbing Stairs
/// https://leetcode.com/problems/climbing-stairs/description/
/// 记忆化搜索
/// 时间复杂度: O(n)
/// 空间复杂度: O(n)
class Solution {

private:
    vector<int> memo;  // 存储计算过的内容

    int calcWays(int n){

        if(n == 0 || n == 1)
            return 1;

        if(memo[n] == -1)  // memo[n] 如果没有计算过
            memo[n] = calcWays(n - 1) + calcWays(n - 2);

        return memo[n];
    }

动态规划

  • 自下而上的解决问题
#include <iostream>
#include <vector>

using namespace std;

/// 70. Climbing Stairs
/// https://leetcode.com/problems/climbing-stairs/description/
/// 动态规划
/// 时间复杂度: O(n)
/// 空间复杂度: O(n)
class Solution {

public:
    int climbStairs(int n) {

        vector<int> memo(n + 1, -1);
        memo[0] = 1;
        memo[1] = 1;
        for(int i = 2 ; i <= n ; i ++)
            memo[i] = memo[i - 1] + memo[i - 2];
        return memo[n];
    }
};

三. 发现重叠子问题 Integer Break

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

解题思路

  • 重叠子问题: 在寻找子问题的最大乘积中得到答案
  • 以4为例: 寻找3, 2, 1的最大乘积

代码

  • 暴力解法: 回溯法
#include <iostream>
#include <cassert>

using namespace std;

/// 343. Integer Break
/// https://leetcode.com/problems/integer-break/description/
/// 暴力搜索
/// 在Leetcode中提交这个版本的代码会超时! (Time Limit Exceeded)
/// 时间复杂度: O(n^n)
/// 空间复杂度: O(n)
class Solution {

private:
    int max3(int a, int b, int c){
        return max(a, max(b, c));
    }

    // 将n进行分割(至少分割两部分), 可以获得的最大乘积
    int breakInteger(int n){

        if(n == 1)
            return 1;

        int res = -1;
        for(int i = 1 ; i <= n - 1 ; i ++)
            res = max3(res, i * (n - i), i * breakInteger(n - i));  
            // n-i  与n-i的分割的最大乘积, 需要比较
        return res;
    }

public:
    int integerBreak(int n) {
        assert(n >= 1);
        return breakInteger(n);
    }
};
  • 记忆化搜索
#include <iostream>
#include <vector>
#include <cassert>

using namespace std;

/// 343. Integer Break
/// https://leetcode.com/problems/integer-break/description/
/// 记忆化搜索
/// 时间复杂度: O(n^2)
/// 空间复杂度: O(n)
class Solution {
private:
    vector<int> memo;

    int max3(int a, int b, int c){
        return max(a, max(b, c));
    }

    // 将n进行分割(至少分割两部分), 可以获得的最大乘积
    int breakInteger(int n){

        if(n == 1)
            return 1;

        if(memo[n] != -1)
            return memo[n];

        int res = -1;
        for(int i = 1 ; i <= n - 1 ; i ++)
            res = max3(res, i * (n - i) , i * breakInteger(n - i));
        memo[n] = res;
        return res;
    }

public:
    int integerBreak(int n) {
        assert(n >= 1);
        memo.clear();
        for(int i = 0 ; i < n + 1 ; i ++)
            memo.push_back(-1);
        return breakInteger(n);
    }
};
  • 动态规划
#include <iostream>
#include <vector>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值