zoukankan      html  css  js  c++  java
  • 多重背包的入门题目HDU1171,2191,2844.

    首先,什么叫多重背包呢?

    大概意思就是:一个背包有V总容量,有N种物品,其价值分别为Val1,Val2……,Val3,体积对应的是Vol1,Vol2,……,Vol3,件数对应Num1,Num2……,Num3

    求背包至多装满的最大总价值。

    其实,首先可以当作01背包来做,这时候复杂度就是O(V*sum(Num[i]));

     简单代码来说就是

      for(int i=1;i<=N;i++)
         for(int j=1;j<=Num[i];j++)
              for(int k=V;k>=Vol[i];k++)
                      dp[k]=max(dp[k],dp[k-Vol[i]]+Val[i]);

    很容易超时。如果M大的话。从大神的背包九讲看到一个化成O(Vsum(log(Num[i])))

    大概意思就是利用先判断是否为完全背包情况,否则二进制压缩进行01背包的步骤。任何一个数目Num[i],可以表示为1+2^1 +……+2^p+Num[i]-2^(p+1)+1 其中确定p的条件是Num[i]-2^(p+1)+1>0,Num[i]-2^(p+2)+1<0

    这样我们就可以将第i件物品化成 logNum[i]件物品,起对应的容量和价值,分别对应相应倍数

    简单代码就是

         int i,j,k;
         for(i=1; i<=N; i++)
                if(Vol[i]*Num[i]>=V)    //n*v>=V 完全背包
                {
                    for(j=Vol[i]; j<=V; j++)   //从val到V
                    {
                        dp[j]=max(dp[j],dp[j-Vol[i]]+Val[i]);
                    }
    
                }
                else
                {
                     k=1;
                    int num_tmp=Num[i];    //取出数目
                    while(k<num_tmp)       //二进制优化
                    {
                        for(j=V; j>=k*Vol[i]; j--)     //k*Vol ,k*Val的背包
                            dp[j]=max(dp[j],dp[j-k*Vol[i]]+Val[i]);
                        num_tmp-=k;
                        k<<=1;
                    }
                    for(j=V; j>=num_tmp*vol[i]; j--)    //num_tmp*Vol[i],num_tmp*Val[i]背包问题
                    {
                        dp[j]=max(dp[j],dp[j-num_tmp*Vol[i] ]+Val[i]);
                    }
    
                }
    之后对于HDU1171,2194,用第一种算法就轻松通过了。

    HDU1171的题目要我们做的就是将总和/2规划成资产“不会大”的学院当成容量背包。

    代码

    #include<stdio.h>
    #include<string.h>
    int dp[250000];
    int val[52];
    int num[52];
    int main(){
    
        int n,i,j,k;
        int sum,V;
        while(scanf("%d",&n)&&n>0){
    
            memset(dp,0,sizeof(dp));
          
            sum=0;
            for(i=1;i<=n;i++){
                scanf("%d%d",&val[i],&num[i]);
                sum+=val[i]*num[i];
            }
            V=sum/2; //B的
            for(i=1;i<=n;i++){
                for(j=1;j<=num[i];j++){
                    for(k=V;k>=val[i];k--){
                        dp[k]=dp[k]>dp[k-val[i]]+val[i]?dp[k]:dp[k-val[i]]+val[i];
                    
                    }
                
                }
            
            }
            printf("%d %d
    ",sum-dp[V],dp[V]);
    
                 
        
        }
    
        return 0;
    
    }

    而HDU2191则是赤裸裸的套上模版,更是轻松过。不过在取数组名字时候真是乱七八糟。我有时候用cost val num,有时候用vol val num不过关系都不大。

    #include<stdio.h>
    #include<string.h>
    int dp[106];
    int cst[100];
    int val[100];
    int num[100];
    int max(int a,int b){
        return a>b?a:b;
    
    
    }
    int main(){
    
        int cases,V,N,i,j,k;
        scanf("%d",&cases);
        while(cases--){
           scanf("%d%d",&V,&N);
           memset(dp,0,sizeof(dp));
           memset(cst,0,sizeof(cst));
           memset(val,0,sizeof(val));
           memset(num,0,sizeof(num));
           for(i=1;i<=N;i++)
               scanf("%d%d%d",&cst[i],&val[i],&num[i]);
               
           for(i=1;i<=N;i++)
               for(j=1;j<=num[i];j++)
                   for(k=V;k>=cst[i];k--){
                       dp[k]=max(dp[k],dp[k-cst[i]]+val[i]);
                   
                   }
    
                   printf("%d
    ",dp[V]);
        
        
        }
        return 0;
    
    
    
    
    }

    对于HDU2844这题目,我错了很多次……大多数都是RE……后来大我AC了我才发现,是我当初的数组开的太小了。

    大概思路就是:令vol=val,来做成背包,能付多少钱嘛,那么dp[i]==i就是刚好付i元,最后统计一下就好啦。

    一开始我试着直接化成01背包,果断TLE了……

    /*******************************************************************************/
    /* OS           : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux
     * Compiler     : GCC  4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
     * Encoding     : UTF8
     * Date         : 2014-03-11
     * All Rights Reserved by yaolong.
    *****************************************************************************/
    /* Description: ***************************************************************
    *****************************************************************************/
    /* Analysis: ******************************************************************
    *****************************************************************************/
    /*****************************************************************************/
    
    #include<stdio.h>
    #include<string.h>
    int dp[100002];
    int val[122];
    int num[122];
    int max(int a,int b)
    {
        return a>b?a:b;
    
    }
    int main()
    {
    
    
       int V,N,i,j,res;
    
    
        while(~scanf("%d%d",&N,&V)&&(N||V))
        {
    
    
            memset(dp,0,sizeof(dp));
    
    
            for(i=1; i<=N; i++)
                scanf("%d",&val[i]);
            for(i=1; i<=N; i++)
                scanf("%d",&num[i]);
    
    
            for(i=1; i<=N; i++)
            {
                if(val[i]*num[i]>=V)    //n*v>=V 完全背包
                {
                    for(j=val[i]; j<=V; j++)   //从val到V
                    {
                        dp[j]=max(dp[j],dp[j-val[i]]+val[i]);
                    }
    
                }
                else
                {
                    int k=1;
                    int num_tmp=num[i];    //取出数目
                    while(k<num_tmp)       //二进制优化
                    {
                        for(j=V; j>=k*val[i]; j--)     //k*val ,k*val的背包
                            dp[j]=max(dp[j],dp[j-k*val[i]]+k*val[i]);
                        num_tmp-=k;
                        k<<=1;
                    }
                    for(j=V; j>=num_tmp*val[i]; j--)    //num_tmp*val[i],num_tmp*val[i]背包问题
                    {
                        dp[j]=max(dp[j],dp[j-num_tmp*val[i]]+num_tmp*val[i]);
                    }
    
    
                }
    
    
            }
    
            res=0;
    
    
            for(i = 1; i<=V; i++)
    
                if(dp[i]==i)
                {
                    res++;
                }
    
            printf("%d
    ",res);
    
    
        }
    
        return 0;
    
    }
    

    后来我又写了一遍,因为我当初把各种背包函数写在外面出错了,其实我一直不明白为什么GCC提交,打注释就有问题……

    /*******************************************************************************/
    /* OS           : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux
     * Compiler     : GCC  4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
     * Encoding     : UTF8
     * Date         : 2014-03-11
     * All Rights Reserved by yaolong.
    *****************************************************************************/
    /* Description: ***************************************************************
    *****************************************************************************/
    /* Analysis: ******************************************************************
    *****************************************************************************/
    /*****************************************************************************/
    
    #include<stdio.h>
    #include<string.h>
    int dp[100002];
    int w[122];    //weight
    int n[122];    //num
    int v[122];    //volume
    int V,N;//Volume of the Pack,Number of things
    
    int max(int a,int b)
    {
        return a>b?a:b;
    
    }
    void ZeroOnePack(int vol,int weight){
        int i;
        for(i=V;i>=vol;i--){
          dp[i]=max(dp[i],dp[i-vol]+weight);
        }
    
    }
    void CompletePack(int vol,int weight){
        int i;
        for(i=vol;i<=V;i++){
          dp[i]=max(dp[i],dp[i-vol]+weight);
        }
    
    }
    void MultiPack(int vol,int weight,int num){
    
    
        if(num*vol>=V){//CompletePack
            CompletePack(vol,weight);
            return ;
        }
        int k=1;
        while(k<num){
            ZeroOnePack(k*vol,k*weight);  //ZeroOnePackage
            num-=k;
            k<<=1;
        }
        ZeroOnePack(num*vol,num*weight);
    
    }
    int main()
    {
        int i,res;
        while(scanf("%d%d",&N,&V),N+V){
             memset(dp,0,sizeof(dp));
             for(i=1;i<=N;i++)
                 scanf("%d",&w[i]);
             for(i=1;i<=N;i++)
                 scanf("%d",&n[i]);
             for(i=1;i<=N;i++)
                MultiPack(w[i],w[i],n[i]);
    
             res=0;
             for(i=1;i<=V;i++)
                if(dp[i]==i){
                   res++;
                }
             printf("%d
    ",res);
    
    
    
        }
    
       return 0;
    
    }
    



  • 相关阅读:
    手工卸载和安装NTKO OFFICE文档控件
    【转】HTMLParser使用详解(4) 通过Visitor访问内容
    【转】OA权限管理的实现(下)
    【转】HTMLParser使用详解(2) Node内容
    XNA4.0 学习笔记一
    Silverlight读取与设置Cookies
    Win8 导航及数据状态保持
    XNA 学习做的第一个小游戏 精灵追逐 (一)
    使用Javascript调用Silverlight
    寻路简单思路
  • 原文地址:https://www.cnblogs.com/dengyaolong/p/3697240.html
Copyright © 2011-2022 走看看