为啥要使用动态规划?什么时候使用动态规划?以下是我的一些理解和心得,如果你感兴趣,就接着往下看吧。
对您有帮助的话记得给我点个赞哟!

动态规划的基本思想

动态规划(Dynamic Programming,DP)算法通常用于求解某种具有最优性质的问题。在这类问题中,可能会有许多可行解,每一个解都对应一个值,我们希望找到具有最优值的解
动态规划算法与分治法类似,其基本思想也是将待求解的问题分解成若干个子问题,先求解子问题,然后从这些子问题的解中 得到原有问题的解。与分治法不同的是,动态规划经分解后得到的子问题往往 不是相互独立 的。

(分治算法也可以解决分解后得到的子问题不是相互独立的情况,只是要对公共子问题进行单独求解。这样会使分治法求解问题的复杂度大大提高。对此类问题如果感兴趣的话可以看我的另外一篇文章——分治法详细讲解。)

为什么要使用动态规划算法?

有些问题明明可以用递归实现,为什么还要用动态规划实现呢?下面我们看一个经典的例子:

例:斐波拉契数列

Fibonacci(斐波拉契数列)问题,它是一个简单而典型的分治问题,Fibonacci数列的递归方程表示为:

递归实现代码

#include<stdio.h>
int main(){int F(int n);printf("%d\n",F(5));return 0;
}
//Fibonacci数列的递归算法
int F(int n){if(n<=1){return 1;}else{return F(n-1)+F(n-2);}
}

该程序实现简单,但是效率非常低。因为在递归调用过程中,有些子问题被反复计算多次,例如 :

其中F(3)被计算了两次
F(4)=F(3)+F(2)
F(3)=F(2)+F(1);

那怎样解决这个问题呢?
如果用一个数组保存已解决的子问题的答案,而在需要的时候再从数组中查找出已求得子问题的答案,这样就可以避免大量的重复计算。这种就是动态规划算法:
动态规划算法代码:

#include<stdio.h>
int main(){int Fibonacci(int n);printf("%d\n",Fibonacci(5));return 0;
}
int Fibonacci(int n){//申请一个数组存放子问题的解 int f[n+1],i;f[0]=1;f[1]=1;for(i=2;i<=n;i++){f[i]=f[i-1]+f[i-2];}return f[n];
}

动态规划算法需要存储各子问题的解,所以它的空间复杂度要大于其他算法,这是一种空间换时间的策略。

怎样使用动态规划算法?

以下是动态规划的求解步骤:

1.分析最优子结构性质(递推关系)
2.递归定义最优值(动态规划核心)
3.自底向上的方式计算出最优值(动态规划的执行过程)
4.根据计算最优值时得到的信息,构造最优解

下面我们将通过一个例子来对比动态规划的应用和贪心算法和动态规划有什么不同。

例:数字三角形

如图所示,有一个群岛,共分为若干层,第1层有一个岛屿,第2层有2个岛屿,……,第n层有n个岛屿。每个岛上都有一块宝,其价值是一个正整数(图中圆圈中的整数)。寻宝者只允许从第一层的岛屿进人,从第n层的岛屿退出,不能后退,他能收集他所经过的所有岛屿上的宝贝。但是,从第i层的岛屿进入第i计1层的岛屿时,有且仅有有2条路径。你的任务是:对于给定的群岛和岛上宝贝的价值,计算一个寻宝者行走一趟所能收集宝贝的最大价值。

思路:
这个问题可以抽象为数字三角形问题,要求从顶部出发,在每一个节点可以选择向左走或者向右走,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
数据存储:
我们可以将数字三角形存储在一个二维数组里。

num 0 1 2 3 4
0 9
1 12 15
2 10 6 8
3 2 18 9 5
4 19 7 10 4 16

贪心算法和动态规划的比较:
贪心算法是自顶向下求解,只能选择眼前对自己最有利的一步,其他的路径看不见。
动态规划是自底向上求解,逐层递推。

贪心算法实现:

我们使用贪心算法没法找到真正的最大和(最优解)。是用贪心算法所走的路径为:9+15+8+9+10=51。贪心算法是自顶向下解决问题的,它只能看到眼前的路,并选择对自己最有利的一步,而看不到其他的路径。

关键:

对于num[i][j],只需比较num[i+1][j]和num[i+1][j+1],哪个数字大选择哪条路.

源代码:

//贪心算法实现数字三角形
#include<stdio.h>
int result=0;
int main(){void tanxin(int a[5][5],int h,int l);//传入数组进行贪心选择 h为行数,l为列数 int i,j;//数组填入采用初始化的形式(今天不是主角)int num[5][5]={0};//全部初始化为0num[0][0]=9;num[1][0]=12;num[1][1]=15;num[2][0]=10;num[2][1]=6;num[2][2]=8;num[3][0]=2;num[3][1]=18;num[3][2]=9;num[3][3]=5;num[4][0]=19;num[4][1]=7;num[4][2]=10;num[4][3]=4;num[4][4]=16;//初始化完毕//输出数组printf("输出初始化过后的数组\n");for(i=0;i<5;i++){for(j=0;j<5;j++){if(num[i][j]!=0)printf("%d  ",num[i][j]);}printf("\n");} //贪心选择开始 tanxin(num,5,5);printf("贪心算法计算的数字和为:%d\n",result);return 0;
}
void tanxin(int a[5][5],int h,int l){//拿到数组进行选择result=a[0][0];int i,j;for(i=1;i<h;i++) {j=i;        if(a[i][j+1]!=0){if(a[i+1][j]>=a[i+1][j+1]) result=result+a[i+1][j];else result=result+a[i+1][j+1];}}return result;
}

动态规划实现

我们自底向上,逐层递推,把较大的数字与上一层相加,把得到的数字存到解的数组中(避免重复计算),加到最后就是本题最优解。

例如:

在图中,19+2>7+2,所以把19与上一层相加


自底向上,逐层向上相加,并把计算的结果存储到结果数组中。
关键:

对于num[i][j],比较num[i-1][j],num[i-1][j-1]哪个大,num[i][j]与哪个相加
题中我从num[3][0]开始,与下面的num[4][0]与 num[4][1]比较,因为num[4][0]比较大,num[4][0]与num[3][0]相加

源代码:

//动态规划实现数字三角形
#include<stdio.h>
int result=0;
int main(){//自底向上,眼着全局 int dongtaiguihua(int a[5][5],int h,int l);//数组填入采用初始化的形式(今天不是主角)int num[5][5]={0};//全部初始化为0num[0][0]=9;num[1][0]=12;num[1][1]=15;num[2][0]=10;num[2][1]=6;num[2][2]=8;num[3][0]=2;num[3][1]=18;num[3][2]=9;num[3][3]=5;num[4][0]=19;num[4][1]=7;num[4][2]=10;num[4][3]=4;num[4][4]=16;//初始化完毕 result=dongtaiguihua(num,5,5);printf("动态规划计算的数字和为:%d\n",result);return 0;
}
int dongtaiguihua(int a[5][5],int h,int l){int i,j;for(i=h-2;i>=0;i--){for(j=0;j<=i;j++){if(a[i+1][j]>a[i+1][j+1]) a[i][j]+=a[i+1][j];else a[i][j]+=a[i+1][j+1];}}return a[0][0];
}

运行结果:


写到最后

感谢您看到这里,对您有帮助的话点个赞再走呀!ε = = (づ′▽`)づ 谢谢!!

超详细!动态规划详解分析(典型例题分析和对比,附源码)相关推荐

  1. IDEA2019版最新配置SVN及上传教程-超详细图文详解

    IDEA2019版配置SVN图文详解 1. 查看svn仓库 调出svn视图: 连接svn服务器: 连接后效果如下: 补充:如果输入正确的连接地址后出现错误-系统找不到指定的文件 请到设置中检查(Fil ...

  2. 系统性详解Redis操作Hash类型数据(带源码分析及测试结果)

    1 缘起 系统讲解Redis的Hash类型CURD, 帮助学习者系统且准确学习Hash数据操作, 逐步养成测试的好习惯, 本文较长,Hash的操作比较多,请耐心看, 既可以集中时间看,亦可以碎片时间学 ...

  3. 动态代理最全详解系列[2]-Proxy生成代理类对象源码分析

      之前我们通过JDK中的Proxy实现了动态代理,Proxy用起来是比较简便的,但理解起来不是那么清晰,是因为我们并没有看见代理类是怎么生成的,代理类怎么调用的被代理类方法,所以下面我们进入源码看一 ...

  4. php读取图片文件流,详解php文件包含原理(读取文件源码、图片马、各种协议、远程getshell等)...

    详解php文件包含原理(读取文件源码.图片马.各种协议.远程getshell等) 作者是namezz (看完图相当于做了一轮实验系列) 现有文件代码如下 1.png (21.16 KB, 下载次数: ...

  5. include详解 shell_详解php文件包含原理(读取文件源码、图片马、各种协议、远程getshell等) ......

    详解php文件包含原理(读取文件源码.图片马.各种协议.远程getshell等) 作者是namezz (看完图相当于做了一轮实验系列) 现有文件代码如下 include和include_once.re ...

  6. 【代码管理】GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流

    GitHub操作总结 : 总结看不明白就看下面的详细讲解. . 作者 :万境绝尘  转载请注明出处 : http://blog.csdn.net/shulianghan/article/details ...

  7. dockerfile源码安装mysql_docker容器详解五: dockerfile实现tomcat环境以及源码安装mysql...

    tomcat 上一节讲到了dockerfile的基础,这一次咱们来作一个小的练习 首先要了解tomcat安装的整个过程 首先搭建 jdk环境: 下载jdk包,解压以后添加环境变量 而后搭建tomcat ...

  8. Android中的单例模式(java单例模式详解,Glide,EventBus,LayoutInfalter的源码单例模式详解)

    一.单例模式 (1)单例模式介绍和定义 ​ 大概意思是保证一个类在任何时候都只有一个实例在内存里,以static形式提供整个项目的访问.在Android系统中常用的地方有:创建一个SQLiteOpen ...

  9. Python分析《权力的游戏》最终季...... | 附源码

    译者 | 刘畅 编辑 | 琥珀 出品 | AI科技大本营(id:rgznai100) [导读]<权力的游戏>最终季已于近日开播,对于全世界翘首以待的粉丝们来说,其最大的魅力就在于" ...

  10. 一份比较详细的 webpack 4.x 手工配置基础开发环境 附源码

    重新书写了博客内容,希望可以更好的呈现该有的知识点. bundle.js 指的是 webpack 打包后的文件. 小剧场 项目经理:我们要开始一个新的项目,裤裆你来负责项目构建吧. 我:好的没问题,经 ...

最新文章

  1. Android利用android:indeterminateDrawable来实现ProgressBar三种方式
  2. 图像界面编程简单窗体创建
  3. 前端开发之JavaScript基础篇四
  4. 使用Google App Engine、Google Closure Library与Clojure编写HTML5应用
  5. NFV和VNF的现状如何
  6. plsql cursor 函数
  7. apache个人主页搭建
  8. P1351-联合权值【树形结构】
  9. 傍腾讯,切阿里,拼多多的千亿瞒天术
  10. 三分钟让你掌握JavaScript中值传递和引用传递的区别
  11. 『算法设计_伪代码』快速排序
  12. wpf 写个简单的控件吧
  13. html颜色趋势,展望下一年的网页设计配色趋势
  14. 浅谈App Hybrid混合开发的五种方案
  15. 【嵌入式基础常识】单片机
  16. 陈一为担任BCF理事
  17. 逻辑教育大厂必备IOS面试突击班
  18. 谷歌的现实、摩托的无奈与联想的接盘
  19. 【华为OD机试真题 JAVA】字符统计及重排
  20. JC-6、OpenCV+Tensorflow入门人工智能图像处理

热门文章

  1. Unity 给物体加贴图
  2. 现代编程语言(3):zig
  3. qpython3可以画图吗_手机qpython3如何画图
  4. ffmpeg statis vs dev技术选型?
  5. 如何在线批量转换图片格式为jpg?
  6. 百度地图 开启 绘制 功能(画圆)
  7. 迪杰斯特拉算法(邻接表求解)
  8. 凡事预则立,不预则废。做事提前5分钟,更多主动权。
  9. enjoy 问候相关
  10. 拼多多产品怎么引流?拼多多商品怎么引更多的流量?