zoukankan      html  css  js  c++  java
  • 多重背包

    【问题描述】

            有N种物品和一个容量为w的背包,第i种物品最多有m[i]件可用,每件容量是c[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    【输入】

              第一行 正整数n和w

             以下n行,每行三个正整数为物品的容量、价值和件数,中间用空格隔开。

    【输出】

    最大价值总和。

    【数据规模】

            1<= n<=100, w<=10000, 1<=m[i]<=10000, 1<=c[i],v[i]<=100

     分析:

    方法1,朴素算法:f[i,j]表示前i种物品装入容量为j的背包中所能得到的最大价值(0<=k<=min(m[i],j div v[i]))。  

             f[i,j]=max{f[i-1,j-k*c[i]]+k*v[i]}   ,0<=k<=min(m[i],j div v[i])。   

             时间复杂度O(n*V*∑m[i])

    方法2,转换为01背包,利用二进制拆分

              假设第i种物品有m[i]件,则可以第i种物品看成m[i]件01背包中的物品,则得到了物品数为Σm[i]的01背包问题,直接求解.时间复杂度:O(V*Σ m[i])。

    二进制拆分:

           m[i]=1+2+4+...+2^k+a   (0<=a<2(k+1),a=n-2^(K+1)+1)

           由于1,2....,2^k可以表示0~2^(K+1)-1中的所有整数(由K位二进制表示),则1,2,4,...,2^k,a可以表示出0~m[i]的所有整数(a与0~2^(K+1)-1的所有整数相加,可以表示出2^(K+1)~n的所有整数)。

           m[i]可以分解出log(m[i])+1个背包,则时间复杂度为O(V*∑log(m[i]))

    方法3,单调队列

        方法1中f[i,j]=max{f[i-1,j-k*c[i]]+k*v[i]}   ,0<=k<=min(m[i],j div v[i])。

       令a[j]=f[i-1,j*c[i]],k=m[i],假设当前重量为c[i]的整数倍,即j mod  c[i]=0 则有

       f[i,(j+k)*c[i]]=max{f[i-1,(j+k)*c[i]-k*c[i]]+k*v[i],f[i-1,(j+k)*c[i]-(k-1)*c[i]]+(k-1)*v[i],...,f[i-1,(j+k)*c[i]+(k-k)*c[i]]+(k-k)*v[i] 

                           =max{f[i-1,j*c[i]]+k*v[i],f[i-1,(j+1)*c[i]]+(k-1)*v[i],...f[i-1,(j+k)*c[i]]}

                           =max{a[j]+k*v[i],a[j+1]+(k-1)*v[i],...,a[j+k]}

    这样还是不方便计算,令b[j]=a[j]-j*v[i]

        则有f[i,(j+k)*c[i]]=max{b[j]+(j+k)*v[i],b[j+1]+(J+K)*v[i],...,b[j+k]+(j+k)*v[i]}

                                   =max{b[j],b[j+1],...,b[j+k]}+(j+k)*v[i]

    这样就可以使用单调队列来求解了,时间复杂度O(nw)。

     (注意,前面的公式推导是假设当前重量为c[i]的整数倍,程序实现时要考虑到所有情况,如,不是c[i]整数倍情况的处理。

    余数有0,1,2,......c[i]-1种情况,c[i]可以选0,1,......m[i]件)。

       for i:=1 to n do 

          for t:=0 to c[i]-1 do

               begin

                ..................

               end;

             

     1 //f[i,(j+k)*c[i]]=max{b[j],b[j+1],...,b[j+k]}+(j+k)*c[i]
     2 uses math;
     3 const
     4   maxn=110;
     5   maxw=11000;
     6 var
     7   n,w:longint;
     8   m,c,v:array[0..maxn] of longint;
     9   f:array[0..maxn,0..maxw] of longint;
    10   procedure init;
    11   var i:longint;
    12   begin
    13     assign(input,'dcb.in');reset(input );
    14     readln(n,w);
    15     for i:=1 to n do readln(c[i],v[i],m[i]);
    16   end;
    17   procedure main;
    18   var
    19     head,tail,x,i,j,t:longint;
    20     qd,qb:array[0..maxn] of longint;
    21   begin
    22     for i:=1 to n do
    23       for t:=0 to c[i]-1 do //枚举所有可能的余数
    24         begin
    25           head:=0;tail:=0;
    26           fillchar(qd,sizeof(qd),0);
    27           fillchar(qb,sizeof(qb),0);
    28           for j:=0 to (w-t) div c[i] do //j*c[i]+t<=w;枚举余数为t,重量为j*c[i]+t时的情况
    29             begin  //维护队列中元素个数<=c[i]的单调队列
    30               x:=f[i-1,j*c[i]+t]-j*v[i];//计算b[j]
    31               while (head<tail)and(qd[tail]<=x) do dec(tail);//保持队列的单调递减
    32               inc(tail);qb[tail]:=j;qd[tail]:=x;//b[j]与下标j分别入队
    33               f[i,j*c[i]+t]:=qd[head+1]+j*v[i];//队首元素(队列中最大的元素)+j*v[i],
    34               if qb[head+1]=j-m[i] then inc(head);//如果正好是第j-m[i]个下标则出队,即构成0~k的循环了。
    35             end;
    36           end;
    37   end;
    38 begin
    39   assign(output,'dcb2.out');rewrite(output);
    40   init;
    41   main;
    42   writeln(f[n,w]);
    43   close(output);
    44 end.
    View Code

    (注意也可以用一维数组实现)

  • 相关阅读:
    【原】使用Spring自带的JdbcTemplate。
    【原】MyBatis执行DDL:create table,drop table等等
    【转】java获取当前路径的几种方法
    【原】Java程序调用远程Shell脚本
    [转载] 使用Kettle进行数据迁移(ETL)
    appium原理
    python之global关键字的用法
    python之selenium定位(xpath)
    android 稳定性monkey测试
    python之selenium随记(几种等待的用法)
  • 原文地址:https://www.cnblogs.com/ssfzmfy/p/3795293.html
Copyright © 2011-2022 走看看