周总结#6-背包问题

几个背包问题的简述:

01背包:

完全背包:

多重背包:

01背包

朴素解法:

#include <iostream>
#include <algo>
#define maxn 1010
using namespace std;

int n,m;
int w[maxn],v[maxn];
int f[maxn][maxn];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++){      //第i个物体的时候 
        for(int j=0;j<=m;j++){  //容量有一点不剩的情况 第j个容量状态 
            f[i][j]= f[i-1][j]; //左半边子集的情况
            if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); //前提是装得下 才会有右边的情况 
        } 
    }
    cout<<f[n][m]<<endl;
    return 0; 
}

优化解法:

#include <iostream>
#include <algorithm>
#define maxn 1010
using namespace std;

int n,m;
int v[maxn],w[maxn];
int f[maxn]; 

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++){  

//二维改成一维 就默认 每一次的f[j]是当前i的 而不是朴素解法之中的赋成i-1的 这样就会出问题: f[i][j-k] 是这一回已经更新过的 所以会一个用两次得情况 
        for(int j=m;j>=v[i];j--){  

//装不下得情况就不用考虑了 那说明就一定没有i 这层循环就跳过去了 
            f[j]=f[j];

//可以 相当于f[i][j]=f[i-1][j]; 因为即便f[i][j-k]已经更新到i个的状态 但是f[x][j]没更新到 仍然是f[i-1][j] 所依无论体积正倒遍历 这个都可以
            if(j>=v[i]) f[j]=max(f[j],f[j-v[i]]+w[i]);

//这个f[j-v[i]]正不可以 因为正的话重量更轻的在这轮i已经都更新过了 这时状态已经包含了i 而后面又多加了一个i 与题目矛盾 想那个右半边子集的两部分也可以     
        }
    }
    cout<<f[m]<<endl;
    return 0; 
}

完全背包:

朴素解法:

#include <iostream>
#include <algorithm>
#define maxn 1010
using namespace std;

int n,m;
int v[maxn],w[maxn];
int f[maxn][maxn];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){  //从1开始是为什么??? 
            f[i][j]=f[i-1][j];
            if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);

//f[i][j-v[i]]的最大值+w[i]就是f[i][j]第一项往后之后的最大值 所以必须从前往后遍历体积 
        }
    }
    cout<<f[n][m]<<endl;
    return 0;

优化算法:

#include <iostream>
#include <algorithm>
#define maxn 1010
using namespace std;

int n,m;
int v[maxn],w[maxn],f[maxn];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=v[i];j<=m;j++){

//装不下的话一定是上一个能装的下的物品状态 所以一维数组没有被遍历的j存的并不会改变还是上一个能装得下的物品的状态 
            f[j]=max(f[j],f[j-v[i]]+w[i]);

//装得下的情况下 左边是上一个物品状态 右边是所有要装i的总共的最大 这些和f[i][j-v[i]]的最大值加一个w[i]相等 而这个f[j-v[i]]在i层算是在f[j]以前确定过了 
        }
    }
    cout<<f[m]<<endl;
    return 0; 

多重背包:

关于二进制表示法:

二进制表示所有连续整数的方法是不同2次幂权值选择0或1,所以如果按照二进制把众多物品打包成若干小背包,每个背包里面有2^n个物品,则用01的原理,也就是选择(1)或者不选择(0)这个小背包就可以把选择物品数的连续整数全都表示出来,而且时间复杂度是o(nlogn)

朴素解法:

#include <iostream>
#include <algorithm>
#define maxn 110
using namespace std;

int n,m;
int v[maxn],w[maxn],s[maxn];
int f[maxn][maxn];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i]>>s[i];
    }
    for(int i=1;i<=n;i++){  

//由于式子多了一项 没有办法照搬前面存储的最大值了 最大值比较次数比较多 需要用到三重循环 
        for(int j=0;j<=m;j++){
            for(int k=0;k<=s[i] && k*v[i]<=j;k++){ //从不放到放k个 
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);  
            }
        }
    }
    cout<<f[n][m];
    return 0;
}

优化解法:

#include <iostream>
#include <algorithm>
#define maxn 25000   
using namespace std;

int n,m;
int v[maxn],w[maxn];
int f[maxn];

int main(){
    cin>>n>>m;
    int cnt=0;
    for(int i=1;i<=n;i++){ //要一个一个输入i参数 然后每个i打成一堆小包 
        int k=1; //用这个来作为二次幂 写在for里面每一次回到1 
        int a,b,s;
        cin>>a>>b>>s;
    while(k<=s){
        cnt++;
        v[cnt]=k*a; //小包里面有k个i 需要小包的体积和价值都乘以这么多 
        w[cnt]=k*b;
        s-=k;  //这个是为了确保小包表示后 所有小包中的i数量加起来不超过k 后面会有比较用上这个 
        k*=2;
        } 
        if(s>0){  //k有可能和s之间有一段空隙 正好不是2的次幂的时候就是 
            cnt++;
            v[cnt]=s*a;
            w[cnt]=s*b;
        }
    }
    n=cnt;
    //拿那么多小包做01背包问题 这些小背包是第1个物体的众多小背包 第2个物体的 一直到第n个物体的所有小背包 累在一起的一个集合 
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
            }
        }
    cout<<f[m]<<endl;
    return 0;
}

P1048 [NOIP2005 普及组] 采药

一个01背包的模板题:

#include <iostream>
#include <algorithm>
#define maxn 1010
using namespace std;

int t,n;
int v[maxn],w[maxn];
int f[maxn];

int main(){
    cin>>t>>n;
    for(int i=1;i<=n;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=t;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[t]<<endl; //输出最后一个物品遍历完 时间正好也满了的状态最大
    return 0; 

P1616 疯狂的采药

完全背包模板题

#include<bits/stdc++.h>
#define LL long long 
using namespace std;

const LL N=1e4+5, M=1e7+5;

LL t,n;
LL v[N],w[N];
LL f[M];

int main(){
    scanf("%lld%lld",&t,&n); 
    for(LL i=1;i<=n;i++){
        scanf("%lld%lld",&v[i],&w[i]);
    }
    for(LL i=1;i<=n;i++){
        for(LL j=v[i];j<=t;j++){  //如果从0开始 中间也有判断为啥会142 
          f[j]=max(f[j],f[j-v[i]]+w[i]); //从前往后 因为需要前面更新过的f[i-1][j-v[i]] f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]) //1 2 3 4 6.。。。。。一直到无穷列举补完 这样的话必有地推关系便是后面着无穷无尽议案长传 
        }
    }      //  f[i][j-v[i] = f[i-1][j-v[i]]+f[i-1][j-2v[i]]+w[i]+...... max= f[i][j-v[i]]
    //max + w[i]
    printf("%lld",f[t]);
    return 0; 

P1049 [NOIP2001 普及组] 装箱问题

本质是01背包,没有说价值是多少但是让求剩余空间的最小值,也就是在包的体积承受范围内,找重量和的最大值,这时可以认为重量就是价值,从而在总重量制约下,找到剩余最少

#include <iostream>
#include <algorithm>
#define maxn 25000
using namespace std;

int n,m;
int v[maxn],w[maxn],f[maxn];

int main(){
    cin>>m>>n;
    for(int i=1;i<=n;i++){
        cin>>v[i];
        w[i]=v[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        } 
    }
    cout<<m-f[m]<<endl;
    return 0;

P1833 樱花

多重背包问题,如果是无穷大的观赏次数就设成很大的数

#include <iostream>
#include <algorithm>
#define maxn 2500000
using namespace std;

int n;
int a,b,c,d;
int v[maxn],w[maxn],s,f[maxn];

int main(){
    scanf("%d:%d %d:%d",&a,&b,&c,&d);
    cin>>n;
    int t=(c*60+d)-(a*60+b);
    int cnt=0;
    for(int i=1;i<=n;i++){
        int k=1;
        cin>>a>>b>>s;
        if(s==0) s=9999999;
        while(k<=s){
            cnt++;
            v[cnt]=a*k;
            w[cnt]=b*k;
            s-=k;
            k*=2;
        }
        if(s>0){
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
    }
    n=cnt;
    for(int i=1;i<=n;i++){
        for(int j=t;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[t]<<endl; 
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值