动态规划算法(Dynamic Programming)

动态规划算法的核心

//动态规划算法的核心就是记住已经解决过的子问题的解

通过动态规划解决斐波那契数列

//斐波那契数列(Fibonacci)
//递归法
int fib(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;
    return fib(n-1) + fib(n-2);//n=6 ,返回8
}

动态规划算法(Dynamic Programming)

//执行流程,可以看出递归,很多重复的节点被执行,其中fib(2)被重复执行5次。
//由于调用每一个函数都要保留上下文,所以空间开销不小。
//通过备忘录把被执行过的子节点保存起来,后边在调用直接使用,节约大量时间。

动态规划算法的两种形式

//1. 自顶向下的备忘录法
//2. 自底向上

 

自顶向下的备忘录法

#include <iostream>
using namespace std;

int fib(int n, int memo[]) {
    if (memo[n] != -1) return memo[n];//递归发现fib(n)已经算出来则保存并返回
    if (n <= 2) memo[n] = 1;
    else memo[n] = fib(n-1,memo) + fib(n-2,memo);
    return memo[n];
}

int main() {
    int n,sum=0;
    scanf("%d",&n);//6
    if (n == 0) sum = 0;
    else {
        int memo[n+1];//创建备忘录保存求出的斐波拉契数列中的每一个值
        for (int i=0; i<=n; i++) memo[i] = -1;//初始化
        sum = fib(n,memo);//调用斐波那契数列函数
    }
    printf("%d ",sum);//8
    return 0;
}

自底向上的动态规划

//自底向上方法也是利用数组保存了先计算的值,为后面的调用服务
int fib(int n) {
    if (n==0) return n;
    int memo[n+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];
}

//或者进一步压缩空间
#include <iostream>
using namespace std;

int fib(int n) {
    if (n==0) return n;
    int memo1 = 0;
    int memo2 = 1;
    int memo3 = 1;
    for (int i=2; i<=n; i++) {
        memo3 = memo1 + memo2;//最终的和
        memo1 = memo2;//第二项赋值给第一项
        memo2 = memo3;//第三项赋值给第二项
    }
    return memo3;
}

int main() {
    int n;
    scanf("%d",&n);//6
    cout << fib(n);
    return 0;
}

 

动态规划的使用

//1. 最优子结构
//如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质
//使用动态规划算法时,用子问题的最优解来构造原问题的最优解。

//2. 重叠子问题
//如果递归算法反复求解相同的子问题,就称为具有重叠子问题(overlapping subproblems)性质。
//在动态规划算法中使用数组来保存子问题的解,这样子问题多次求解的时候可以直接查表不用调用函数递归。

 

原文链接:https://blog.csdn.net/u013309870/article/details/75193592

上一篇:每日一练_113(2021.10.1) 扰乱字符串(leetcode)。


下一篇:状态压缩dp