动态规划算法之0-1背包问题

Posted zqq277

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划算法之0-1背包问题相关的知识,希望对你有一定的参考价值。

动态规划算法:
    动态规划就是一个填表的过程。该表记录了已解决的子问题的答案。求解下一个子问题时会用到上一个子问题的答案。比如01背包问题:假如有1个背包,背包容量是10,有5个物品,编号为1,2,3,4,5,他们都有各自的重量和价格。要求在不超过背包容量的情况下,使背包装载物品的价值最大。现将问题拆分为五个子问题。
1.背容=10,从1号物品中找出该问题的解
2.背容=10,从1号,2号物品中找出该问题的解
3.背容=10,从1号,2号,3号物品中找出该问题的解
4.背容=10,从1号,2号,3号,4号物品中找出该问题的解
5.背容=10,从1号,2号,3号,4号,5号物品中找出该问题的解

思路:
   我们可以将1,2,3,4,5子问题的答案都存入一张表中。因为求解2子问题,需要用到1子问题的答案(2的每一步方案要与1的每一步方案比较,如何2的该步方案优于1所对应的方案。则将2的这步方案标为可行。如果不优于1的,或者不满足问题的约束条件,则舍弃该方案。继续沿用该步所对应的1的方案作为该步的方案)。求解3子问题,需要用到2子问题的答案,一直递推到求解5子问题,需要用到4子问题的答案。而5子问题就是原问题。5子问题的答案就是最终原问题的解。
以01背包问题为例,作讲解。给出问题参数:

1.程序是一行一行的进行填表的。f[0][0,1,2,3,4.....]......f[5][0,1,2,3,4....]  (程序初始化的过程就是将第0行的所有数填为0,这是符合现实情况的。它表明当前背包没有装物品,当前背包的价值是0)

2.拿f[3][8]=11来说明填表的过程。f[3]表明i=3(当前子问题有3个物品可选,分别是1,2,3号物品),f[3][*]的值就是第3个子问题的解。我要选的3号物品的重量是6,它的价值是5,所以我会找到它的前6列的上一行所对应的背包的价值(f[2][2])+5(当前要选的3号物品的价值)=11,值为11>f[2][8]=9,代表我选了1,3的方案要优于选1,2的这种方案,所以我将11填入到表格【如果f[2][2]+5<9,则将9填入表格】-------------------这里需要说明一下f[2][8]=9的含义:2子问题的一个解是1,2选择1,2号物品所对应的背包价值为9。------恰巧我们在解决3子问题时1,2,3需要计算比较这几种方案,选1,2号物品1,2>>>>背价=?,背包价格是多少,1,3>>>>背价=?,1,2,3>>>>背价=?而1,2>>背包价格在2子问题中已给出,因此我们可以在3子问题中直接用。为何不考虑2,3呢?就是拿2号和3号组队放入背包?因为在2子问题中已经记载了:【如果要单选一个背包的话,选择1号获得的价值要比2号获得的价值大----我们追溯到f[2][2]=6是整么来的,背包容量是2的时候,[1,2]号物品只能有一个放入背包,放入1,背包的价值是6,f[1][2]=6的计算是在1子问题中已经求解出来的,2子问题可以直接用该值,而不用再重复计算。放入2号,背值是3,3<6,所以沿用上一个子问题的解作为该步的答案,所以f[2][2]是6,而不是3,所以它相当于定义说:下一个子问题在求解的过程中,如果遇到只能从2号和1号物品中选择一个物品装入背包时,请选择1号物品】

3.其实将上述的描述写成代码,就是两层for循环(遍历n,再嵌套遍历c),加上两个判断(1.当前子问题的可行的一个方案与上一个子问题的对应的可行方案谁最优。2.连续变量j的值是否达到了跳跃点的值,达到了才更新当前背包的价值):代码如下


#include<stdio.h>

#define mmax(a,b) (a)>(b)?(a):(b)
int m[2][1000001]; 

int knap(int n,int c,int *w,int *v)
    for(int i=0;i<=n;i++)
        m[0][0]=0;
        m[0][i]=0;
    
    for(int i=1;i<=n;i++)
        int newi=i%2;
        int ordi=(i+1)%2;
        for(int j=1;j<=c;j++)
            if(j>=w[i])
                m[newi][j]=mmax(m[ordi][j],m[ordi][j-w[i]]+v[i]);
            
            else m[newi][j]=m[ordi][j];
        
    
    return m[n%2][c];

int main()
    int n,c,w[1001],v[1001];
    scanf("%d %d",&n,&c);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    printf("%d",knap(n,c,w,v));

结果如图所示:

 

以上是关于动态规划算法之0-1背包问题的主要内容,如果未能解决你的问题,请参考以下文章

动态规划算法之0-1背包问题

求动态规划0-1背包算法解释

基础算法——动态规划0/1背包问题

动态规划算法实现部分——0/1背包问题

图解算法-怎么用动态规划解决0-1背包问题

动态规划之背包问题