• 多重背包(学习笔记)


    宝物筛选_NOI导刊2010提高(02)

    樱花

    这两道都是洛咕上多重背包的模板题,然后第二道题前面还要稍微处理一下字符串???

    二进制拆分法(第一题的代码)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=100005;
    int val[N],w[N],f[N];
    int main(){
        int n=read(),W=read(),tot=0,ans=0;
        for(int i=1;i<=n;++i){
            int v=read(),weight=read(),c=read();
            for(int j=1;j<=c;j<<=1){
                val[++tot]=j*v;
                w[tot]=j*weight;
                c-=j;
            }
            if(c)val[++tot]=c*v,w[tot]=c*weight;
        }
        for(int i=1;i<=tot;++i)
            for(int j=W;j>=w[i];--j)
                f[j]=max(f[j],f[j-w[i]]+val[i]);
        for(int i=1;i<=W;++i)ans=max(ans,f[i]);
        printf("%d
    ",ans);
        return 0;
    }
    
    

    单调队列优化(第一题代码)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=105;
    int a[N],b[N],c[N],q[40005],pre[N][40005];
    int main(){
    	int n=read(),m=read();
    	for(int i=1;i<=n;++i)b[i]=read(),a[i]=read(),c[i]=read();
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<a[i];++j){
    			int l=1,r=0;
    			for(int k=j;k<=m;k+=a[i]){
    				while(l<=r&&k-q[l]>c[i]*a[i])++l;
    				while(l<=r&&pre[i-1][q[r]]-q[r]/a[i]*b[i]<pre[i-1][k]-k/a[i]*b[i])--r;
    				q[++r]=k;
    				pre[i][k]=pre[i-1][q[l]]+(k-q[l])/a[i]*b[i];
    			}
    		}
    	int ans=0;
    	for(int i=1;i<=m;++i)ans=max(ans,pre[n][i]);
    	printf("%d
    ",ans);
        return 0;
    }
    
    

    单调队列优化法(第二题的代码)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=10005;
    int v[N],w[N],c[N],q[100005],f[100005];
    int main(){
        string s1,s2;cin>>s1>>s2;
        int ans1,ans2;
        for(int i=0;i<s1.size();++i){
            if(s1[i]==':'){
                int cnt1=0,cnt2=0;
                for(int j=0;j<=i-1;++j){
                    cnt1=cnt1*10+s1[j]-'0';
                }
                for(int j=i+1;j<s1.size();++j){
                    cnt2=cnt2*10+s1[j]-'0';
                }
                ans1=cnt1*60+cnt2;
                break;
            }
        }
        for(int i=0;i<s2.size();++i){
            if(s2[i]==':'){
                int cnt1=0,cnt2=0;
                for(int j=0;j<=i-1;++j){
                    cnt1=cnt1*10+s2[j]-'0';
                }
                for(int j=i+1;j<s2.size();++j){
                    cnt2=cnt2*10+s2[j]-'0';
                }
                ans2=cnt1*60+cnt2;
                break;
            }
        }
    //以上对字符串的处理,只是为了求出背包体积m
        int n=read(),m=ans2-ans1;
        for(int i=1;i<=n;++i){
            w[i]=read(),v[i]=read(),c[i]=read();
            if(c[i]==0)c[i]=1000;
        }
        for(int i=1;i<=m;++i)f[i]=-1e9;
        for(int i=1;i<=n;++i){
            for(int j=0;j<w[i];++j){
                int l=1,r=0,maxk=(m-j)/w[i];
                for(int k=maxk-1;k>=max(maxk-c[i],0);--k){
                    while(l<=r&&f[j+q[r]*w[i]]-q[r]*v[i]<=f[j+k*w[i]]-k*v[i])--r;
                    q[++r]=k;
                }
                for(int k=maxk;k>=0;--k){
                    while(l<=r&&q[l]>k-1)++l;
                    if(l<=r)f[j+k*w[i]]=max(f[j+k*w[i]],f[j+q[l]*w[i]]+(k-q[l])*v[i]);
                    if(k-c[i]-1>=0){
                        while(l<=r&&f[j+q[r]*w[i]]-q[r]*v[i]<=f[j+(k-c[i]-1)*w[i]]-(k-c[i]-1)*v[i])--r;
                        q[++r]=k-c[i]-1;
                    }
                }
            }
        }
        int ans=0;
        for(int i=1;i<=m;++i)ans=max(ans,f[i]);
        printf("%d
    ",ans);
        return 0;
    }
    
    

    一般来说,二进制拆分法比较容易理解也比较好写,一般的题目用二进制拆分就够了,单调队列的时间复杂度可能更加稳定更加优秀.

  • 相关阅读:
    链表及其各种函数操作的实现方法
    插入排序
    欧几里得算法
    Cookie和Session
    RestTemplate 中文乱码
    java8 按两个属性分组,并返回扁平List; stream排序
    Spring 读取资源
    linux搭建Git
    IDEA 快捷键
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11240542.html
Copyright © 2020-2023  润新知