动态规划之线性动规钢条切割问题

问题描述

线性规划是一类问题。目标函数为特定变量的线性函数,约束是这些变量的线性不等式(standard form)或等式(slack form),目的是求目标函数的最大值或最小值。这类动态规划是平时比较常见的一类动态规划问题。

假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, …单位:美元),下面给出了价格表样例:

这里写图片描述
切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大。
当然,如果长度为n英寸的钢条价格Pn足够大,最优解可能就是完全不需要切割。

解法

我们可以把他分解成两个子问题,然后两个子问题的最优解相加,再比较所有相加的和里选出最优的。每个子问题也可以往下继续分成两个子问题,层层分下去。比如我现在长度为5,那么就可以分成1+4、2+3。1+4里面的4又可以划分子问题,分成1+3、2+2、依此循环下去求出每个子问题的最优解后,与原来本数字的价格比较,谁更大要谁,那么总问题也就解决了
动规表达式:
max[n]=price[n]; (n=0)
max[n]=max(price[n],Ri+Rn-i) (n>0)
注:R代表大利润

使用递归实现

     static int DP(int n){
         int temp=0;
         if(n==0){
             return price[n];
         }

         for(int i=0;i<=(n/2);i++){

             temp=DP(i)+DP(n-i-1);
             max[n]=max(price[n],temp); 
         }
         return max[n];
     }

测试本页数据通过,测试4的时候,for循环跑了8次。感觉效率很低,很多重复子问题都进行了求解,所以改进一下本算法,存储子问题的解,每次进行检测,让重复子问题不再进行二次求解,也就是我们说的动态规划思想。牺牲一定的空间,换取时间。

带备忘录的自上而下求法。

 static int DP(int n){
         int temp=0;
         if(r[n]>=0){
             return r[n];
         }
         if(n==0){
             return price[n];
         }

         for(int i=0;i<=(n/2);i++){

             temp=DP(i)+DP(n-i-1);
             max[n]=max(price[n],temp);
         }
         r[n]=max[n];
         return max[n];

测试长度为4时循环只跑了五次,大大提高了效率。

本题另外一种写法
动规表达式:
Rn=max(Pi+Rn-i)

int Memorized_cut_rod_Aux(int p[], int n, int r[])  
{  
    int q;  
    int i;  
    if (r[n]>=0)//是否子问题重复 
    {  
        return r[n];  
    }  

    if (n==0)  
    {  
        q=0;  
    }  
    else  
    {  

        q=-1;  
        for (i=1; i<=n; i++)  
        {  

            q=max(q,p[i]+Memorized_cut_rod_Aux(p,n-i,r));  
        }  
    }  
    r[n]=q;  
    return q;  
}  

由以上钢条切割问题我们发现,动态规划有时并不是必须的,但是使用常规方法效率极其低下。而动态规划之所以能提高程序的效率,是因为我们避免了重复子问题的求解。我们使用动态规划总是在子子问题的基础上求解子问题,再由子问题出求解最终的答案,而这一切的一切关键在于子问题的建立。

猜你喜欢

转载自blog.csdn.net/hjsir/article/details/78355540