超详细!动态规划详解分析(典型例题分析和对比,附源码)
利用数据工具进行详细的比赛分析 #生活乐趣# #运动乐趣# #运动赛事分析#
为啥要使用动态规划?什么时候使用动态规划?以下是我的一些理解和心得,如果你感兴趣,就接着往下看吧。
对您有帮助的话记得给我点个赞哟!
动态规划的基本思想
动态规划(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);} } 123456789101112131415
该程序实现简单,但是效率非常低。因为在递归调用过程中,有些子问题被反复计算多次,例如 :
其中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]; }
12345678910111213141516动态规划算法需要存储各子问题的解,所以它的空间复杂度要大于其他算法,这是一种空间换时间的策略。
怎样使用动态规划算法?
以下是动态规划的求解步骤:
1.分析最优子结构性质(递推关系)
2.递归定义最优值(动态规划核心)
3.自底向上的方式计算出最优值(动态规划的执行过程)
4.根据计算最优值时得到的信息,构造最优解
下面我们将通过一个例子来对比动态规划的应用和贪心算法和动态规划有什么不同。
例:数字三角形如图所示,有一个群岛,共分为若干层,第1层有一个岛屿,第2层有2个岛屿,……,第n层有n个岛屿。每个岛上都有一块宝,其价值是一个正整数(图中圆圈中的整数)。寻宝者只允许从第一层的岛屿进人,从第n层的岛屿退出,不能后退,他能收集他所经过的所有岛屿上的宝贝。但是,从第i层的岛屿进入第i计1层的岛屿时,有且仅有有2条路径。你的任务是:对于给定的群岛和岛上宝贝的价值,计算一个寻宝者行走一趟所能收集宝贝的最大价值。
思路:
这个问题可以抽象为数字三角形问题,要求从顶部出发,在每一个节点可以选择向左走或者向右走,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
数据存储:
我们可以将数字三角形存储在一个二维数组里。
贪心算法和动态规划的比较:
贪心算法是自顶向下求解,只能选择眼前对自己最有利的一步,其他的路径看不见。
动态规划是自底向上求解,逐层递推。
我们使用贪心算法没法找到真正的最大和(最优解)。是用贪心算法所走的路径为: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; }
1234567891011121314151617181920212223242526272829303132333435363738394041 动态规划实现我们自底向上,逐层递推,把较大的数字与上一层相加,把得到的数字存到解的数组中(避免重复计算),加到最后就是本题最优解。
例如:
在图中,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]; }
12345678910111213141516171819202122232425262728运行结果:
写到最后
感谢您看到这里,对您有帮助的话点个赞再走呀!ε = = (づ′▽`)づ 谢谢!!
网址:超详细!动态规划详解分析(典型例题分析和对比,附源码) https://www.yuejiaxmz.com/news/view/248668
相关内容
旅行商问题(动态规划方法,超级详细的)10个超有趣的经典数据分析案例!让你轻松了解数据分析!——九数云BI
【深度学习】深度学习语音识别算法的详细解析
自动调温除湿机工作原理详解与分析
详细学习除湿机系统课件,全面解析除湿机分类、结构、原理、计算选型
热点资讯最新互动详情深度解读分析
2024年数码相机行业发展现状分析 数码相机行业市场规模及未来趋势分析
个人经营收款码新规实施 移动支付用户规模和市场交易规模分析
AI助手豆包、文小言、通义、Kimi的全方位对比分析
LED照明产品的散热技术分析(详解)