zoukankan      html  css  js  c++  java
  • [转][算法]01背包问题

    原链接:http://www.cnblogs.com/justinzhang/archive/2012/04/10/2441199.html

        记得大二参加数学建模的时候,开始接触了动态规划,在听过张老师的课后,当时使用matlab写了背包问题的代码,记得使用递归实现的。总之,对这个问题认识得非常的不清楚。以前强制记了好几次代码,过一段时间又忘了。虽然每次看书的时候还是可以回想起解题思路,但是心里总有种不踏实的感觉,由于上次腾讯笔试的刺激,发现这么多年以来,自己做的事情都有点南辕北辙、舍本逐末。计算机核心的离散数学、编译原理、组合数学、自动机理论,被放在了一边,从不过问。而去追求一些花哨的东西。真的是勿以往之不谏啊~~所以,虽然自己天天都在努力的工作,但是心里始终有不踏实的感觉,总是又被谴责鼻子走的感觉,那是因为自己根本没有抓住本质的东西,所以才会如此被动~~~计算机虽然已经和数学分得很开了,但是计算机毕竟产生于数学,所以逃避数学的做法是万万不可的。如果背离了数学而去搞计算,那么高度肯定是上不去了,以前认为只要把算法学好了就够了,现在才发现,离散数学、组合数学、自动机理论是如此的重要,在以后的学习和工作中,一定要勤思考才行。。

         背包问题有0-1背包问题和fraction背包问题,前者规定每个物品要么选,要么不选,而fraction knanpsack允许选取一个物品的一部分,0-1背包问题是NP难的,而fraction knapsacks的复杂度是O(n*logn), 只需要将单位物品的价值按降序排列,利用贪心策略选取即可得到最优解。

         给定一个背包,容量为C,有n个物品,重量为n维行向量w,价值为n维行向量v. |v|=|w|=n, n维向量y=[0,1,0….yn]代表物品的选法,其中的没有元素yi,要么是0、要么是1,为零代表选取第i个物品,为0表示不选取第i个物品。 目标函数是:Max(Sum(vi*yi)), 约束条件是sum(wi*yi) <=C, 其中 1=< i <=n.

         背包问题具有最优子结构,令f(n,C)代表,有n个待选物品,背包容量为C时的最优解,此时物品选择向量为y=[y1,y2,…yn], 那么当yn=1时,y’=[y1, y2, …yn-1],必然为f(n-1, C-wn)的物品选择向量,当yn=0时,必然为f(n-1,C)的最优物品选择向量。所以背包问题可以由动态规划来求解。

        根据上面的分析,我们可以得到如下的递归式:

        当wn>C时,  f(n,C)=f(n-1,C);

        当wn<=C时,f(n,C) = max(f(n-1,C), vn+f(n-1, C-wn) );

        初始条件为:f(i, 0) = 0; f(0,i) = 0; f(0,0) = 0;

     

        根据上面的分析用递归实现的0-1背包代码如下:

    ?
    1:  /*
      2:  *
      3:  *时间:22012年4月10日19:46:27
      4:  *作者:JustinZhang
      5:  *Email:uestczhangchao@gmail.com
      6:  */
      7:   
      8:  #include <iostream>
      9:  #include <iomanip>
     10:  using namespace std;
     11:   
     12:  int w[]={1,3,4,5};//物品重量数组
     13:  int v[]={2,30,44,20};//物品价值数组
     14:  int C=5;//背包容量
     15:  int y[4]={-1,-1,-1,-1};//解向量,y[i]=1表示选取物品,y[i]=0表示不选取物品
     16:   
     17:   
     18:  int f(int n, int C)
     19:  {
     20:      if (n==0 || C==0)//当物品数量为0,或者背包容量为0时,最优解为0
     21:      {
     22:          return 0;
     23:      }
     24:      else
     25:      {
     26:          //从当前所剩物品的最后一个物品开始向前,逐个判断是否要添加到背包中
     27:          for (int i=n-1; i>=0;i++)
     28:          {
     29:              //如果当前要判断的物品重量大于背包当前所剩的容量,那么就不选择这个物品
     30:              //在这种情况的最优解为f(n-1,C)
     31:              
     32:              if (w[i]>C) 
     33:              {
     34:                  y[i]=0;
     35:                  return f(n-1,C);
     36:              }
     37:              else
     38:              {
     39:                  //如果当前待判断的物品重量wi<C,那么就选取f(n-1,C)和vi+f(n-1,C-wi)中的最大值
     40:                  int tmp1 = f(n-1,C);//不选择物品i的情况下的最优解
     41:                  int tmp2 = v[i] + f(n-1,C-w[i]);//选择物品i的情况下的最优解
     42:                  
     43:                  //返回选择物品i和不选择物品i中最优解大的一个
     44:                  if (tmp1 > tmp2)
     45:                  {
     46:                      y[i]=0;//这种情况下表示物品i未被选取
     47:                      return tmp1;
     48:                  }
     49:                  else
     50:                  {
     51:                      y[i]=1;//物品i被选取
     52:                      return tmp2;
     53:                  }
     54:   
     55:              }
     56:          }
     57:   
     58:      }
     59:   
     60:  }
     61:   
     62:   
     63:  int main()
     64:  {
     65:   
     66:      int maxvalue = f(4,5);
     67:      for (int i=0; i<4; i++)
     68:      {
     69:          if (y[i]==1)//为1表示相应的物品被选取
     70:          {
     71:              cout << "Object "<< i+1 << " is selected. " << "It's Vaule is " << setw(2)<< v[i] \
     72:                  << " It's Weight is"<< setw(2)<<w[i] << endl; 
     73:          }
     74:      }
     75:   
     76:      cout << "Maximum Value is: "<< maxvalue << endl;
     77:      return 0;
     78:  }
     79:   
     80:   
      

    运行结果如下:


    在算法导论上,背包问题的伪代码如下:



           我们可以看到复杂度为O(nW),初看之下还以为这个算法的复杂度是多项式时间,但是书中明明说0-1背包问题是NP难问题啊,难道书中说错了吗?其实这里忽略的问题是,在上面的程序中n才是输入规模,而W并不是输入规模,因为它是背包的容量,而背包的数量一直都是为1的,如果在物品数量为n的情况下,背包的容量为2^n,那么这个算法的复杂度就是O(n*2^n), 所以这个问题是NP难的~~
  • 相关阅读:
    不会全排列算法(Javascript实现),我教你呀!
    驰骋页面,谁主沉浮-也谈清除浮动
    你不知道的parseInt
    Javascript函数重载,存在呢—还是存在呢?
    在这个看脸的世界,该如何优雅的创建JS对象
    Python 函数的使用小结
    Python 集合(set)的使用总结
    Python 文件操作
    python 中字典的操作(增、删、改、查)
    python 中list的操作(循环、切片、增、删、改、查、反转、排序)
  • 原文地址:https://www.cnblogs.com/iyjhabc/p/2987466.html
Copyright © 2011-2022 走看看