zoukankan      html  css  js  c++  java
  • 【纪中模拟2019.08.18】【JZOJ6309】完全背包

    题目链接

    题意:

      有$n$种物品各有无限个,背包空间为$m$,第$i$种物品的体积为$a_i$,价值为$b_i$。求在不超过背包容量的前提下,取得的最大价值。

      $nle 10^6,quad mle 10^{16},quad a_i,b_ile 100$且均为正整数。

    分析一:

      贪心一:

      实际上最多有$100 imes 100=10^4$种物品,可以考虑去重,这样$n$的规模下降至$10^4$。

      贪心二:

      如果两种物品的体积相同,我们肯定不会装价值更低的那种物品,这样$n$的规模下降至$100$。

      转化法:

        由于$m$过于巨大,然而$a_i$相比较小,我们可以想到,性价比最优的物品一定会出现极多次,这样最终方案肯定可以分割成很多完全相同的小方案(全部装同一种物品),再在剩下的容量里精打细算。因此我们先做小容量(最大可支持的范围)的背包,再来组成最终方案。

      综上,我们先做$nle 100$,$mle 2 imes 10^5$的完全背包,设$f_i$表示背包容量为$i(1le i le 2 imes 10^5)$时能取得的最大价值,则有$$f_m=mathop{max}limits_{i=1}^{2 imes 10^5}{lfloor m/i floor imes f_i + f_{m\%i}}$$

    实现一(100分):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define IL inline
    using namespace std;
    typedef long long LL;
    const int N=100;
    const int M=2e5;
    
        int n;
        LL m,w[103];
        LL f[M+3];
    
    int main(){
        freopen("backpack.in","r",stdin);
        freopen("backpack.out","w",stdout);
    
        scanf("%d%lld",&n,&m);
        memset(w,0,sizeof w);
        for(int i=1;i<=n;i++){
            LL x,y;
            scanf("%lld%lld",&x,&y);
            w[x]=max(w[x],y);
            
        }
        
        memset(f,0,sizeof f);
        for(int i=1;i<=N;i++)
            for(int j=i;j<=M;j++)
                f[j]=max(f[j],f[j-i]+w[i]);
        
        LL ans=0;
        for(int i=1;i<=M;i++)
            ans=max(ans,m/i*f[i]+f[m%i]);
        
        printf("%lld",ans);
    
        return 0;
    
    }
    View Code

     

    分析二:

      前两个贪心策略同分析一。

      贪心三:

      我们已经知道,背包容量巨大,物品体积较小,性价比高的某一种物品一定会大量出现在最终方案中,现在我们想要确定它确切的出现次数。分析一还是偷了个懒,并没有做这件事情,只是由这个性质让答案自己“脱颖而出”,效率可不算高。接下来给出一种方法来确定“零”“整”的“分界线”。

      设在性价比最高的物品中$a$最小的为$s$号,且最终方案中取了$x$种非$s$号物品,则一定有$$x<a_s ag{1}$$

      先给出一个引理:

      给定任意$n$个整数,它们之中存在若干个整数的和为$n$的倍数。

      证明很简单,对这$n$个整数做前缀和,设为${S_n}$,那么对于$S_0,\,S_1\,S_2dots\,S_n$这$n+1$个整数,至少有两个数模$n$同余,它们的差就是$n$的倍数。

      回到$(1)$,考虑反证法:若$xge a_s$,那么一定存在若干物品的价值之和是$a_s$的倍数,用$s$号物品替换它们一定不劣。

      由此可知,非$s$号物品的总体积一定不会超过$100 imes a_s$。所以$s$号物品至少有$lfloor m/a_s floor -100$个。

      那么做法就是先取一下$s$号物品,剩下的空间做完全背包。

      有一点要注意:在找性价比最高的物品时,必须初始化为实际物品,不能用虚拟物品初始化,具体请看代码注释。

    实现二(100分):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define IL inline
    using namespace std;
    typedef long long LL;
    const int N=100;
    const int M=1e6;
    
        int n;
        LL m,w[103];
        LL f[M+3];
    
    IL bool eql(LL w1,LL v1,LL w2,LL v2){
        return v1*w2==v2*w1;
        
    }
    
    IL bool betr(LL w1,LL v1,LL w2,LL v2){
        return v1*w2>v2*w1;
        
    }
    
    int main(){
        freopen("backpack.in","r",stdin);
        freopen("backpack.out","w",stdout);
    
        scanf("%d%lld",&n,&m);
        memset(w,0,sizeof w);
        for(int i=1;i<=n;i++){
            LL x,y;
            scanf("%lld%lld",&x,&y);
            w[x]=max(w[x],y);
            
        }
        
        LL tw=1,tv=w[1];/////////////WA 50pts: LL tw=101,tv=0;
        for(LL i=2;i<=N;i++)
        if(betr(i,w[i],tw,tv)||(eql(i,w[i],tw,tv)&&i<tw)){
            tw=i;
            tv=w[i];
            
        }
        
        LL cnt=m/tw-100;
        m-=cnt*tw;
        LL tmp=tv*cnt;
        memset(f,0,sizeof f);
        for(LL i=1;i<=N;i++)
            for(LL j=i;j<=m;j++)
                f[j]=max(f[j],f[j-i]+w[i]);
        
        printf("%lld",tmp+f[m]);
    
        return 0;
    
    }
    View Code

     

    小结:

      数据规模不同,造就不同题目。应学会灵活处理。

  • 相关阅读:
    【2017-5-24】WebForm 条件查询
    【2017-5-24】WebForm 分页功能
    【2017-5-22】Application ViewState RepeaterCommand用法
    【2017-5-21】问题总结 Session,Cookie,登录状态保持
    【2017-5-20】传值基础 复合控件
    【2017-5-18】WebForm Repeater的使用 简单控件
    JavaScript Bind()趣味解答 包懂~~
    无题
    移动端横屏(beta)
    常用CSS居中
  • 原文地址:https://www.cnblogs.com/Hansue/p/11374000.html
Copyright © 2011-2022 走看看