ACM日记——算法导论.15章 动态规划(一)

来源:互联网 发布:山东男人啪的表现知乎 编辑:程序博客网 时间:2024/05/21 14:03
动态规划思想与分治思想类似,都是通过将大的问题分解成一个个更小的问题,通过更小的问题的解决来解决比它更大一层的问题,最终得到最后的结果,不过与普通分治不同,动态规划每次都会选取局部最优解,确切的说是“一个问题的最优解”,而不是“最优解”,因为一个问题可能有多个最优解。所以可以减少许多的重复过程,节约大量的时间,不过,动态规划dynamic programming 故名思义,这里的programming是表格的意思,也就是说每次计算都会把结果保留在一个表格中,等需要的时候返回这个值,典型的时空权衡,通过空间的牺牲来换取可观的时间上的效益。
例如,一个问题通过普通递归分治法解决,每增加一层递归树计算次数就会以指数级增长,这是很可怕的,最后2^n-1个叶结点,每个叶结点都会对应一种可能情况,势必会浪费大量的时间,而动态规划的时间复杂度是O(n^2)的,其实就是两层for循环,动态规划的问题最核心就是推导出状态转移方程。
动态规划可以通过两种方式来实现,算法导论上写的是带备忘的自顶向下法,另一种是自底向上法,听上去怪唬人的,其实就是递归和递推实现,递归的时候可以通过创建栈的时候把结果保存在栈桢,达到了“备忘”的效果(个人猜想),而递推比较便于理解,其实一般情况下,递归可以和循环相互转化的。(吐槽一下,老外就是爱唬人,2块钱一斤的大白糖非得说成碳水化合物,鄙视一个,说实在的,算法导论写的真挺好,就是专业性太强了,太过注重数学推导,这在一定程度上给算法初学者造成一定的障碍,不过,坚持坚持,时间长了也就习惯啦)。
鄙人是弱鸟一只,所以呢,动态规划我尽量用递推的方式去实现,因为递推是显式的,相比之下更易于理解,并且在运行的过程中可以避免大量的创建栈而去浪费不必要的时间和空间(其实对我来说前者是主要原因-_-||),一会我用算法导论动态规划第一个例子 钢条分割 来演示动态规划的思想。
题目的大意就是说某公司生产钢条,并且钢条的价格随长度的变化而变化,要求让你把某一长度的钢条分割成若干份,使得这段钢条的价值尽可能的高,这里我就用数组a[]来表示长度了。
钢条长度A[]
1
2
3
4
5
6
7
8
9
10
对应价格
$
1
5
8
9
10
17
27
20
24
30
 
比如当n = 4的时候,你可以选择不切割,那么你将收益9$,如果按1切割,你可以1+1+1+1=4(把四份分为四个1份,收益为4,同下),可以1+2+1=7,当然智商没问题的人会选择2+2=10,所以说,这是通过局部最优来达到最终结果最优解,如果数据大一些,比如9,你可以选择全是一份,一个两份,两个两份,一个三份的......,一共有2^9-1种选法,这要是递归去求解,势必会浪费许多的计算时间,下面是我用递推解决这个动态规划问题的源码:
首先用数组a来存储十种长度所对应的价格
int a[] = {0,1,5,8,9,10,17,17,20,24,30};//a[0]=0,因为长度为0的钢条卖不了钱

然后写一个简单的返回二者最大值的themax函数

int themax(int a,int b){    return a>b?a:b;}<pre name="code" class="html">定义一些需要用到的变量int i,j,n,p;//n代表长度 p代表当前长度所能达到的一个问题的最优解scanf("%d",&n);//输入钢条长度nInt r[n] = {0};//通过这个数组来达到记录的目的For( i = 1 ; i <= n ; i++ ){P=-9999;//先把当前最优解设为负无穷(暂且就拿-9999凑合了)For( j = 1 ; j <= i ; j++)P = themax( p , a[j] + r[i-j] ); R[j] = p; //把每一步的局部最优解p的值赋给数组对应的r[j]中}

最后,r[n]中的值就是问题从钢条不分割到全分割完毕时达到的最优解。
附上我的全部代码:

#include <cstdio>#include <iostream>using namespace std;int a[] = {0,1,5,8,9,10,17,17,20,24,30};int r[100]={0},b[100]={0};int themax(int a,int b){    return a>b?a:b;}int main(){        int i,j,n,p;//n代表长度 p代表当前长度    while(scanf("%d",&n)!=EOF)    {        for(i=1;i<=n;i++)            {                p=-1000;                for(j=1;j<=i;j++)                {                    p=themax(p,a[j]+r[i-j]);//p是当前子段的最优子结构                    printf(" a[%d]+r[%d] = %d + %d = %d\n",j,i-j,a[j],r[i-j],a[j]+r[i-j]);                }                r[--j] = p;                printf("\n r[%d] = %d\n",j,r[j]);            }          for(i=1;i<=n;i++)  printf("r[%d] --> %d\t a[%d] --> %d\n",i,r[i],i,a[i]);    }    return 0;}
哦了,先写到这里,等我把“带备忘的自顶向下法”实现了再更新吧
END
    by:haoran 
14.3.25


0 0
原创粉丝点击