多重背包(学习笔记)

宝物筛选_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\n",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\n",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\n",ans);
    return 0;
}

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

posted on 2019-07-24 20:07  PPXppx  阅读(131)  评论(0编辑  收藏  举报