问题描述
线性规划是一类问题。目标函数为特定变量的线性函数,约束是这些变量的线性不等式(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;
}
由以上钢条切割问题我们发现,动态规划有时并不是必须的,但是使用常规方法效率极其低下。而动态规划之所以能提高程序的效率,是因为我们避免了重复子问题的求解。我们使用动态规划总是在子子问题的基础上求解子问题,再由子问题出求解最终的答案,而这一切的一切关键在于子问题的建立。