背包问题入门记录

感谢赵宗昌老师

01背包

//01 暴力 
void dfs(int i,int j,int s){//对物品i进行决策,j为剩余背包重量
    if(i==n+1){
        ans=max(ans,s);
        return;
    } 
    dfs(i+1,j,s);//不选
    if(j>=w[i]) dfs(i+1,j-w[i],s+c[i]);//能装下就选物品i 
}
int main(){
    ...
    dfs(1,m,0);
    ...
} 
//01 二进制枚举
int k=1<<n;
for(int i=0;i<k;i++){
    int W=0,C=0;
    for(int j=0;j<n;j++)
        if(i&(1<<j)){
            W+=w[j+1];
            C+=c[j+1];
        }
    if(W<=m) ans=max(ans,C);
} 
cout<<ans<<endl; //O(2^n)  适合n<=20
//01-1
for(int i=1;i<=n;i++)//前i个物品用了j的容量 
    for(int j=0;j<=m;j++)
        f[i][j]=f[i-1][j];
        if(j>w[i]) f[i][j]=max(f[i][j],f[i-1][j-w[i]+c[i]); 
//01-2
for(int i=1;i<=n;i++)
    for(int j=0;j<=m;j++)
        for(int k=0;k<=1;k++)
            if(j>k*w[i]) f[i][j]=max(f[i][j],f[i-1][j-w[i]*k]+k*c[i]); 
//01-3滚动数组
for(int i=1;i<=n;i++)
    for(int j=m;>=w[i];j--)//防止一个物品选多次 
        f[j]=max(f[j],f[j-w[i]]+c[i]);
cout<<f[m]<<endl; 


完全背包

//完全-1
for(int i=1;i<=n;i++)
    for(int j=0;i<=m;j++){
        f[i][j]=f[i-1][j];
        if(j>=w[i]) f[i][j]=max(f[i][j],f[i][j-w[i]]+c[i]);
    } 
//完全-2
for(int i=1;i<=n;i++)
    for(int j=0;j<=m;j++)
        for(int k=0;k<=j/w[i];k++)
            f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]);
//完全-3
for(int i=1;i<=n;i++)
    for(int j=w[i];j<=m;j++)
        f[j]=max(f[j],f[j-w[i]+c[i]);

完全背包问题转化为01背包问题来解。
最简单的想法是,考虑到第i种物品最多选V/w[i]件,于是可以把第i种物品转化为V/w[i]件费用及价值均不变的物
品,然后求解这个01背包问题。这样完全没有改进基本思路的时间复杂度,但这毕竟给了我们将完全背包问题转化
为01背包问题的思路:将一种物品拆成多件物品。
高效的转化方法是:把第i种物品拆成费用为w[i]*2^k、价值为c[i]*2^k的若干件物品,其中k满足w[i]*2^k<V。这
是二进制的思想,因为不管最优策略选几件第i种物品,总可以表示成若干个2^k件物品的和。这样把每种物品拆成
O(log(V/w[i])+1)件物品,是一个很大的改进。后面多重背包也用到这种方法。

多重背包

//多重背包-1  ->完全背包
//O(V*Σs[i])
for(int i=1;i<=n;i++)
    for(int j=0;j<=v;j++)
        for(int k=0;k<=s[i];k++)
            if(j>=k*w[i]) f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]);
//滚动数组
for(int i=1;i<=n;i++) 
    for(int j=v;j>=0;j--)
        for(int k=0;k<=s[i];k++)
            if(j>=k*w[i]) f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
            
//多重背包-2  ->01背包+滚动数组 
//第i件物品,看做s[i]件一样的物品i,变成了s[1]+..+s[n]件物品的0-1背包,每个背包取还是不取
//O(V*Σs[i]) 
for(int i=1;i<=n;i++)
    for(int j=1;j<=s[i];j++)
        for(int k=v;k>=w[i];k--) 
            f[k]=max(f[k],f[k-j*w[i]]+j*c[i]);
//二进制
//O(V*sum(log(s[i])))
for(int i=1;i<=n;i++){
    int x,y,z,k;
    cin>>x>>y>>z;
    for(k=1;z>0;k=2*k){
        int d=min(k,z);
        if(d>0){
            w[++sn]=d*x;
            c[sn]=d*y;
        }
        z=z-k;
    }
}
for(int i=1;i<=sn;i++)
    for(int j=v;j>=w[i];j--)
        f[j]=max(f[j], f[j-w[i]]+c[i]);

混合背包一般完全背包单独求解,多重背包和01背包用01背包求解

原文地址:https://www.cnblogs.com/lcan/p/9454460.html