多重背包转换成0-1背包

http://acm.hdu.edu.cn/showproblem.php?pid=2191

多重背包特点:
一种物品有C个(既不是固定的1个,也不是无数个)

优化的方法:
运用神奇的二进制,进行物品拆分,转化成01背包
物品拆分,把13个相同的物品分成4组(1,2,4,6)
用这4组可以组成任意一个1~13之间的数!
原理:一个数总可以用2^k表示
而且总和等于13,所以不会组成超过13的数

所以可将一种有C个的物品拆分成1,2,4,...,2^(k-1),C-(2^k-1)
然后进行01背包
const  int  Max_N = 1008 ;

int  N , V ;
int  dp[Max_N] , w[Max_N] , v[Max_N] ;

void Dvide(int _v , int  _w , int S){
     int x = 1 ;
     while(S >= x){
         w[++N] = _w * x ;
         v[N] = _v * x ;
         S -= x ;
         x <<= 1 ;
     }
     if(S != 0){
          w[++N] = _w * S ;
          v[N] = _v * S ;
     }
}

int  main(){
     int T  , M , i  , j  , _w , _v , _s;
     cin>>T ;
     while(T--){
          cin>>V>>M ;
          N = 0 ;
          memset(dp , 0 , sizeof(dp)) ;
          while(M--){
              scanf("%d%d%d" , &_v , &_w , &_s) ;
              Dvide(_v , _w , _s) ;
          }
          for(i = 1 ; i <= N ; i++){
              for(j = V ; j >= v[i] ; j--){
                      dp[j] = max(dp[j] , dp[j-v[i]] + w[i]) ;
              }
          }
          printf("%d\n" , dp[V]) ;
     }
     return 0 ;
}


http://acm.hdu.edu.cn/showproblem.php?pid=1059

1 , 2 , 3 ,4 ,5 ,6  有x1 , x2 ,x3 ,x4 ,x5 ,x6个 ,问能否平分成2份和相等。

const int Max_N = 6 * 400 ;

int N ;
int v[Max_N] ;

void Divide(int _v , int S){
     int x = 1 ;
     while(S >= x){
         v[++N] =  x * _v ;
         S -= x ;
         x <<= 1 ;
     }
     if(S > 0){
         v[++N] = S * _v ;
     }
}

int x[7] ;
bool dp[210008] ;

int main(){
    int sum , k = 1  , i , j ;
    while(scanf("%d" ,&x[1])){
          sum = x[1] ;
          for(i = 2 ; i <= 6 ; i++){
                scanf("%d" ,&x[i]) ;
                sum += i*x[i] ;
          }
          if(sum == 0)  break ;
          printf("Collection #%d:\n" , k++) ;
          if(sum & 1){
               puts("Can't be divided.") ;
               puts("") ;
               continue ;
          }
          sum >>= 1 ;
          N = 0 ;
          fill(dp , dp+sum+1 , 0) ;
          dp[0] = 1 ;
          for(i = 1 ; i <= 6 ; i++)
              Divide(i , x[i]) ;
          for(i = 1 ; i <= N ; i++){
              for(j = sum ; j >= v[i] ; j--){
                  dp[j] |= dp[j-v[i]] ;
              }
          }
          printf("%s\n" , dp[sum]? "Can be divided." : "Can't be divided.") ;
          puts("") ;
    }
    return 0 ;
}


http://acm.hdu.edu.cn/showproblem.php?pid=2844

多重背包问题,用n个物品,每个物品有价值v[i],每个物品数量限制为c[i]

这道题是问,用所有的硬币能够在m的范围内最多可以组合成多少种价值


对于每个硬币而言:

 

IF 价值×数量>=V

       THEN 取这个硬币的次数相当于无限制,可以考虑成完全背包

ELSE

       THEN 考虑成0-1背包(二进制优化)

const int Max_N =  100008 ;
const int Max_M =  108 ;

int  v[Max_N] , c[Max_N] ;
int  dp[Max_N] ;
int  V , N ;

void  ZoreOnePack(int v , int w){
      for(int i = V ; i >= v ; i--)
           dp[i] = max(dp[i] , dp[i - v] + w) ;
}

void  CompletePack(int v , int w){
      for(int i = v ; i <= V ; i++)
           dp[i] = max(dp[i] , dp[i - v] + w) ;
}

int  DP(){
     int x , i  , ans = 0  ;
     fill(dp , dp+1+V , 0) ;
     for(i = 1 ; i <= N ; i++){
          if(v[i] * c[i] >= V)
              CompletePack(v[i] , v[i]) ;
          else{
              x = 1  ;
              while(c[i] >= x){
                   ZoreOnePack(v[i]*x , v[i]*x) ;
                   c[i] -= x ;
                   x <<= 1 ;
              }
              if(c[i] > 0)
                   ZoreOnePack(v[i]*c[i] , v[i]*c[i]) ;
          }
     }
     for(i = 1 ; i <= V ; i++)
         ans += dp[i] == i  ;
     return ans ;
}

int  main(){
     int i ;
     while(cin>>N>>V){
          if(N == 0 && V == 0)  break ;
          for(i = 1 ; i <= N ; i++)
               scanf("%d" ,&v[i])   ;
          for(i = 1 ; i <= N ; i++)
               scanf("%d" ,&c[i])  ;
          if(V <= 0)
             puts("0") ;
          else
             cout<<DP()<<endl ;
     }
     return 0 ;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值