您的位置:首页 > 其它

算法学习之动态规划(一)

2016-04-11 21:09 260 查看
动态规划的两个条件是最优子结构和子问题重叠。

最优子结构:一个问题的最优解包含其子问题的最优解。换句话说,用子问题的最优解可以构造原问题的最优解。例如钢条切割问题。

子问题重叠:子问题空间必须足够小,问题的递归算法会反复地求解相同的子问题。如背包问题。

动态规划求解时的方法:带备忘的自顶向下法自底向上法

我理解的带备忘的自顶向下法就是比如需要的是子问题的最小值,然后在存储结果的数组中先赋成无穷大,然后在从上到下递归时,若没有这个值,就采取递归来计算它(通常需要遍历所有的情况取最小值);若这个值已经在数组中保存,就直接利用它。“备忘”也就是这个存储之前计算结果的数组。

个人认为自底向上法更好理解一些,即从最小的子问题出发,一步一步将所有的子问题都求解,存在数组中,最后利用这些已经求解的子问题计算出原问题的最优解。

步骤:

1.刻画一个最优解的结构特征。通常也就是将父问题进行划分,看是否满足最优子结构。

2.递归定义最优解的值。一般是将递归式写出来,将m[i,j]用m[i-1,j]或另外的参数之类的表示出来,可能还要分i==j和i < j的条件。

3.用递归式计算最优解的值。

4.用上面的方法构造一个最优解。

钢条切割问题:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,……,n),求钢条切割方案,使得销售收益rn最大。注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割。

首先,我们需要确定这个问题满足最优子结构。让我们来这样想:将钢条从左边切割下一段长度为i的一段,剩下右边长度为n-i的一段可以递归来进行切割。也就是对左边进行长度为0-n的遍历,选取最大的,即为最优解。

递归式如下:

rn = max{pi + r(n-i)}(1<=i<=n)

算法:

1.自底向上法(迭代) 用r[0..n]保存收最优收益,用s[0..n]来保存第一段切割长度

r[0] = 0
for j = 1 to n
q = -∞
for i = 1 to j
//q = max{q, pi+r[j-i]}
if q < pi+r[j-i]
q = pi+r[j-i]
s[j]=i  //用来保存第一段切割长度
r[j] = q


2.带备忘的自顶向下法 (递归)

r[0] = 0
for i = 1 to n
r[i] = -∞
f(p,n,r)

f(p,n,r)
if r
>= 0
return r

if n == 0
q = 0
else
q = -∞
for i = 1 to n
if q < pi+f(p,n-i,r)
q = pi+f(p,n-i,r)
s[j] = i
r
= q
return q


具体C代码如下:

#include<stdio.h>
#define M 1000
int p[11] = {0,1,5,8,9,10,17,17,20,24,30};
int r[M] = {0};
int s[M] = {0};
int BOTTOM_UP_CUT_ROD(int n)//自底向上法
{
int i,j,q = 0;
for(i = 0; i <= n; i++){
r[i] = -1;//负无穷大
s[i] = 0;
}
r[0] = 0;
for(j = 1; j <= n; j++){
q = -1;//负无穷大
for(i = 1; i <= j; i++){
if(q < p[i]+r[j-i]){
q = p[i]+r[j-i];
s[j] = i;
}
}
r[j] = q;
}
}
int MEMOIZED_CUT_ROD(int n)//带备忘的自顶向下法
{
int i,j;
for(i = 0; i <= n; i++){
r[i] = -1;//负无穷大
s[i] = 0;
}
return MEMOIZED_CUT_ROD_AUX(n);
}
int MEMOIZED_CUT_ROD_AUX(int n)
{
int i,q;
if(r
>= 0)
return r
;
if(n == 0)
q = 0;
else{
q = -1;//负无穷大
for(i = 1; i <= n; i++){
if(q < p[i]+MEMOIZED_CUT_ROD_AUX(n-i)){
q = p[i] + MEMOIZED_CUT_ROD_AUX(n-i);
s
= i;
}
}
}
r
= q;
return q;
}
void print(int n)
{
printf("对应的切割方案为:");
while(n > 0){
printf("%d ",s
);
n = n - s
;
}
printf("\n");
}
int main()
{
int n,k;
printf("请输入钢条长度:");
scanf("%d",&n);
BOTTOM_UP_CUT_ROD(10);
printf("自底向上:%d\n",r
);
print(n);
printf("自顶向下:%d\n",MEMOIZED_CUT_ROD(n));
print(n);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: