zoukankan      html  css  js  c++  java
  • 动态规划算法1——背包问题

    本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。

    动态规划是一个OI选手都熟悉的算法,同时也是刚接触时比较难理解的。

    今天,我为大家分享一类比较简单的动态规划问题——背包问题

    背包问题(Knapsack problem)是在1978年提出的,它都可以类似的描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。

    背包问题主要有三种分支:

    1、01背包;

    2、完全背包;

    3、多重背包。

    其中01背包比较容易,(甚至可以用贪心来做,但这里不予考虑)。

    它是这样的:给定一组物品,每种物品都有自己的重量和价格(“性价比”一般不相同),但是都只可以选一个(一个物品选一次)。在限定的总重量内,我们如何选择,才能使得物品的总价格最高。

    因为只能选一次,所以我们可以先枚举第i个物品是否能加入背包,然后如何判断能不能加入呢?在循环里再嵌套一层循环枚举当前背包已经使用的空间,如果这个空间比当前物品的重量还小肯定不对,就不合算了。每次枚举都要进行一次决策,取最优值。需要注意的是,这个空间j的枚举必须从后往前枚举,不然就可能会取多个相同的物品,显然这不行,违反了原意(想一想,为什么?)。

    好了,应该大家到这里都大概听懂了吧?如果没听懂,也没关系,可以反复地思考,或者参考其他的文章。如果你的代码能力比较强,也可以尝试阅读代码,说不定有所帮助。为了降低代码的空间复杂度,我们对决策数组f[ ]进行了压维,使它降成了一维。Cpp代码如下(复制代码请点图标):

     1 /*
     2 01背包
     3 适用于输入格式如下的问题,出现问题请自行调整:
     4 第一行两个整数M, N分别表示背包空间与物品总数;
     5 第2到N+1行每行两个整数,分别表示这类物品每个的价值和所需空间。 
     6 */
     7 #include<cstdio>
     8 const int MAXM=10001,MAXN=51;
     9 int m,n;
    10 int w[MAXN],c[MAXN];
    11 int f[MAXM];
    12 int max(int x,int y)
    13 {
    14     return x>y?x:y;
    15 }
    16 int main()
    17 {
    18     scanf("%d%d",&m,&n);
    19     for(int i=1;i<=n;i++)
    20         scanf("%d%d",&w[i],&c[i]);
    21     for(int i=1;i<=n;i++)
    22         for(int j=m;j>=w[i];j--)
    23             f[j]=max(f[j],f[j-w[i]]+c[i]);
    24     printf("%d
    ",f[m]);
    25     return 0;
    26 }

    那么,我们接下来讲的就是完全背包。完全背包和01背包类似,只不过每种物品有无限多件,你爱取几件取几件!

    它们的代码也是极其的相似!仅仅是for循环的顺序从逆序变成了顺序。(刚才说的取多件,其实是变成了完全背包)。Cpp代码:

     1 /*
     2 完全背包,输入规则:
     3 第一行两个整数M, N分别表示背包空间与物品总数;
     4 第2到N+1行每行两个整数,分别表示这类物品每个的价值和所需空间。 
     5 */
     6 #include<cstdio>
     7 const int MAXM=10001,MAXN=51;
     8 int m,n;
     9 int w[MAXN],c[MAXN];
    10 int f[MAXM];
    11 int max(int x,int y)
    12 {
    13     return x>y?x:y;
    14 }
    15 int main()
    16 {
    17     scanf("%d%d",&m,&n);
    18     for(int i=1;i<=n;i++)
    19         scanf("%d%d",&w[i],&c[i]);
    20     for(int i=1;i<=n;i++)
    21         for(int j=w[i];j<=m;j++)
    22             f[j]=max(f[j],f[j-w[i]]+c[i]);
    23     printf("%d
    ",f[m]);
    24     return 0;
    25 }

    最后一个最最最难的多重背包,它是每个物品都有有限多个!

    它需要用到二进制思想进行拆分(其实有点像拆分成多个01背包,然后再把答案合并)!Cpp代码如下:

     1 /*
     2 多重背包,输入格式: 
     3 第一行,一个整数n,物品数量;
     4 第二行,n个整数,第i个整数表示第i个物品的价格bi;
     5 第三行,n个整数,第i个整数表示第i个物品的数量ci;
     6 第四行,一个整数m,背包空间。
     7 */
     8 #include<cstdio>
     9 const int MAXM=10001,MAXN=6001;
    10 int v[MAXM],w[MAXM];
    11 int f[MAXN];
    12 int n,m,p;
    13 int max(int x,int y)
    14 {
    15     return x>y?x:y;
    16 }
    17 int main()
    18 {
    19     scanf("%d%d",&n,&m);
    20     for(int i=1;i<=n;i++)
    21     {
    22         int x,y,s,t=1;
    23         scanf("%d%d%d",&x,&y,&s);
    24         for(;s>=t;t<<=1)
    25         {
    26             v[++p]=x*t;
    27             w[p]=y*t;
    28             s-=t;
    29         }
    30         v[++p]=x*s;
    31         w[p]=y*s;
    32     }
    33     for(int i=1;i<=p;i++)
    34         for(int j=m;j>=v[i];j--)
    35             f[j]=max(f[j],f[j-v[i]]+w[i]);
    36     printf("%d
    ",f[m]);
    37     return 0;
    38 }

    这篇文章到此就结束了。希望对大家有所帮助,谢谢!

  • 相关阅读:
    POJ3311Hie with the Pie(floyd传递+DP,状态压缩)
    POJ1185炮兵阵地(DP状态压缩)
    POJ3254Corn Fields (状态压缩or插头DP)
    eBPF Tracing 入门教程与实例
    因为 Java 和 Php 在获取客户端 cookie 方式不同引发的 bug
    DRDS 数据恢复重磅发布,全方位保障您的数据安全
    前沿 | 全球最具影响力开源数据库峰会开幕在即 阿里云精彩议题先睹为快
    MaxCompute 预付费标准版VS套餐版
    DTCC 2019 | 深度解码阿里数据库实现 数据库内核——基于HLC的分布式事务实现深度剖析
    从 Apache ORC 到 Apache Calcite | 2019大数据技术公开课第一季《技术人生专访》
  • 原文地址:https://www.cnblogs.com/frankchenfu/p/6445843.html
Copyright © 2011-2022 走看看