0-1背包问题

    题目要求
    给定n种物品和一个背包。物品i的质量为wi,其价值为vi,背包的最大载重量为C。编写一个程序,求解如何装载背包里的物品,使得装入背包的物品总价值最大。

    题目分析
    (1)这是一个经典的问题。解决方法有很多,一般常用方法有:动态规划,分支限界,回溯法等。在这里先介绍一种简单的方法–回溯法。
    (2)如果把这n种物品的取舍状态用一个向量表示{ x1 x2 ,…, xi ,…, xn },其中 xi 只有两种可能的取值1或0。当 xi =1是,表示将第i的物品放入背包,当 xi =0时,表示不讲第i个物品放入背包。
    (3)怎样才能找到这样的向量呢?一种比较直观的方法是应用回溯法在两棵由0-1构成的解空间树中进行查找。
    为什么呢?因为由概率论的知识可知,一个n维二值向量的可能取值为 2n 种,因此它的解空间相当于根结点不同的两个n层满二叉树(满二叉树有 2n1 个叶结点,一个叶结点代表一种结果)。所以,只要搜索这两棵树,找出向量的所有可能解,再判断这些可能的解向量是否满足0-1背包的条件,找出符合条件的解向量,问题就解决了。
    可用剪枝操作减掉不可行的结点及以下分支。

    注意:
    因为要在解空间中价值总量最高的的可能解,而这样的解不一定只有一个 (0-1背包问题可能有很多解),因此需要在搜索0-1解空间树的过程中记录下搜索的路径,然后比较哪一(些)条路径对应的装包方案总价值最高,这一(些)条搜索路径对应的装包方案就是最终的解。但是这样做的空间复杂度较高,因为每搜索到一条可能解路径都要将它记录下来,因此空间占用量太大。

    一种比较简单的方法是分两次回溯搜索解空间树,第一次搜索计算出背包可装载物品的最大价值量,第二次搜索计算出满足这个最大价值量的全部装包方案。这样做虽然有些费时,但减少了空间使用。

    在实际的算法设计中,并不需要真的建立所谓的解空间树,可以通过一个递归过程模拟这个搜索过程。


    算法描述如下:

    int x[100];
    //找到物品装包的最大价值量
    knap_1(int n,int flag,int c,int *price)
    {
         int i,j,p;
         if(isOverLoad(n,c))  return;  //超过背包的装载上限C,剪枝
         if(n==flag)                   //搜索完一条可能路径
         {
            p=getval(n);              //获得这种装包方案的总价质量
            if(*price<p)               //用price存储最高值
                  *price=p;
            return;
         }
    
         for(i=0;i<=1;i++)
         {
               x[n]=i;               //生成可能解路径
               knap_1(n+1,flag,c,price);
         }
    }
    
    knap_2(int n,int flag,int c,int price)
    {
            int i,j,p;
            if(isOverLoad(n,c))  return;
            if(n==flag)
            {
                 p=getval(n);
                 if(price==p)
                 {
                     输出装包方案;
                     return;              //回溯继续搜索
                 }
                 return;
            }
    
            for(i=0;i<=1;i++)
           {
               x[n]=i;               //生成可能解路径
               knap_2(n+1,flag,c,price);
           }
    }       

    算法分析:
    该算法包括两个函数knap_ ()1和knap_2().
    函数isOverLoad()的作用是判断当前x中记录的搜索路径对应的物品重量是否超过装载上限C,若超载,则返回本次递归调用,相当于剪枝。参数*price为一指针型变量,是用来返回最大价值量的。返回值被knap_2()函数利用(这也是为什么一个是指针变量一个是不是指针变量)


    程序如下:

    #include"stdio.h"
    int x[100];
    int val[100]={-1};       //存放物品单价
    int weight[100]={-1};    //存放物品重量
    
    //限重函数
    int isOverLoad(int n,int c)
    {
          int i,w=0;
          for(i=0;i<n;i++)
             w=w+weight[i]*x[i];
          if(w>c)   return 1;
          else    return 0;
    }
    
    //求总价值函数
    int getval(n)
    {
        int i,v=0;
        for(i=0;i<n;i++)
            v=v+x[i]*vai[i];
        return v;
    }
    
    //求最高价值函数
    knap_1(int n,int flag,int c,int *price)   //n为x数组下标,flag为物品数量
    {
           int i,j,p;
           if(isOverLoad(n,c))   return;   //剪枝
           if(n==flag)
           {
              p=getval(n);
              if(*price<p)     *price=p;
              return;
           }
    
          for(i=0;i<n;i++)
          {
               x[n]=i;
               knap_1(n+1,flag,c,price);
          }
    }
    
    //找最佳方案函数
    knap_2(int n,int flag,int c,int price)
    {
         int i,j,p;
         if(isOverLoad(n,c))   return;
         if(n==flag)
         {
               p=getval(n);    
               if(price==p)
               {
                    printf("---------bag----------\n");
                    for(j=0;j<n;j++) 
                        if(x[j]==1)
                         {
                            printf("| |p%d:       | |\n",j);
                            printf("| |weight:%2d kg| |\n",weight[j]);
                            printf("| |price:%2d $ | |\n",val[j]);
                         }          
                  printf("--------------------\n");
                  getche();
                  return;
               }
    
         for(i=0;i<n;i++)
        {
            x[n]=i;
            knap_2(n+1,flag,c,price);
        }
    }
    
    
    main()
    {
         int price=0,i,n,c;
         printf("Input the number of products:\n");
         scanf("%d",&n);
         printf("Input the weight of each product\n");
         for(i=0;i<n;i++)
            scanf("%d",&weight[i]);
         printf("Input the limit weight the bag can overload:\n");
         scanf("%d",&c);
         printf("Input the price of each product\n");
         for(i=0;i<n;i++)
             scanf("%d",&val[i]);
         knap_1(0,n,c,&price);
         knap_2(0,n,c,price);
         printf("The grass price :%d $",price);
         getche();
    }
    
    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值