当前位置: 首页 > news >正文

动态规划

动态规划(Dynamic Programming)基本思想

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式 。

看一个例子,比如求斐波那契(Fibonacci)数列的第N项

斐波那契数列:1  1  2  3  5  8 13  21.....

fib(1) = 1

fib(2) = 1

fib(3) = 2

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

那一般我们都会使用到递归的方法来求第N项的值

#include <iostream>
using namespace std;

int fib(int n)
{
    if (n == 0) 
        return 0;
    if (n == 1) 
        return 1;
    else 
        return fib(n - 1) + fib(n - 2);
}

int main()
{
    int n; 
    cin >> n;
    cout << fib(n) << endl;
    return 0;
}

时间复杂度为2^n

使用递归算法那就是如下图这样的计算方式。我们可以发现这种计算方式存在许多重叠子问题,如fib(n-2)、fib(n-3)等都重复计算了,这样就产生生了不必要的计算,所以使用动态规划思想的的话,我们可以定义一个F[ ]向量,将fib(n-2)、fib(n-3)等的结果存到其中,这样做的最终结果就是我们只需要计算左边部分。时间复杂度也因此降为O(n)。

从递归到动态规划

1、自上而下(备忘录法)

时间复杂度、空间复杂度皆为O(n)。

#include <iostream>
#include <vector>
using namespace std;
int fib(int n)
{   
    vector<int> F(n + 1, 0);
    if (n == 1 || n == 2) 
        return 1;
    if (F[n] != 0) 
        return F[n];
    else
    {
        F[n] = fib(n - 1) + fib(n - 2);
    }
    return F[n];
}
int main()
{
    int n; 
    cin >> n;
    cout << fib(n) << endl;
    return 0;
}

2、自下而上(回溯法)

从最后一步的递归(fib(0))开始反推结果。

时间复杂度O(n)、空间复杂度O(1)。

#include <iostream>
using namespace std;

int fib(int n) 
{
    if (n <= 1) 
        return n;
    int F[10000];
    F[1] = 1;
    F[2] = 1;
    for (int i = 3; i <= n; i++) 
    {
        F[i] = F[i - 1] + F[i - 2];
    }
    return F[n];
}

int main()
{
    int n; 
    cin >> n;
    cout << fib(n) << endl;
    return 0;
}

从第N向的表达式来看,我们只需要维护两个数就可以了,不需要记录整个序列。

int fib(int n) 
{
    if (n <= 1) 
        return n;
    int F[2];
    F[0] = 1;
    F[1] = 1;
    for (int i = 3; i <= n; i++) 
    {
        int temp = F[0] + F[1];
        F[0] = F[1];
        F[1] = temp;
    }
    return F[1];
}

时间复杂度O(n),空间复杂度O(1).

相关文章:

  • 算法分析
  • 编写一个函数,实现将char类型的字符串,循环右移n个位置
  • 类构造、析构、赋值函数示例
  • 数组指针指针数组
  • LeeCode 20.有效的括号
  • LeeCode 26 删除排序数组中的重复项,返回数组新长度
  • LeeCode 27 移除元素,返回数组新长度
  • LeeCode 2125 合并两个(K个)有序链表
  • (10)STL算法之搜索(二) 二分查找
  • 单例模式(懒汉模式、饿汉模式)
  • 工厂模式抽象工厂
  • 链表翻转
  • LRU缓存算法
  • 判断单链表中是否存在环
  • LeeCode61 旋转链表
  • CentOS 7 防火墙操作
  • CSS 专业技巧
  • echarts的各种常用效果展示
  • FineReport中如何实现自动滚屏效果
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • QQ浏览器x5内核的兼容性问题
  • socket.io+express实现聊天室的思考(三)
  • ViewService——一种保证客户端与服务端同步的方法
  • 从PHP迁移至Golang - 基础篇
  • 反思总结然后整装待发
  • 模型微调
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 在Mac OS X上安装 Ruby运行环境
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • #{}和${}的区别?
  • ()、[]、{}、(())、[[]]命令替换
  • (003)SlickEdit Unity的补全
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)换源+apt-get基础配置+搜狗拼音
  • (二)丶RabbitMQ的六大核心
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (一一四)第九章编程练习
  • (原)本想说脏话,奈何已放下
  • (转)Oracle存储过程编写经验和优化措施
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 验证控件和javaScript的冲突问题
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .Net中的设计模式——Factory Method模式
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • [ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)
  • []FET-430SIM508 研究日志 11.3.31
  • [Angular] 笔记 8:list/detail 页面以及@Input
  • [c]扫雷