几大背包模版介绍

九讲背包

(1)01背包

题目:01背包

这是最基础的背包问题,每种物品仅有一件,可以选择放或不放。

二维写法:

void solve(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i];
    }
    //dp[i][j]代表i个物品,背包容量为j的最大价值
    for (int i = 1; i <= n; ++i) {
        //枚举容量,最大容量为m
        for (int j = 1; j <= m; ++j) {
            dp[i][j] = dp[i-1][j];  //首先让dp[i][j]等于i-1个物品容量为j最大价值
            if(j >= v[i]){  //判断需不需要更新,(这个物品能不能放下)
                dp[i][j] = max(dp[i][j],dp[i-1][j-v[i]] + w[i]);//判断拿和不拿哪个大
            }
        }
    }
    cout<<dp[n][m];
}

一维写法

void solve(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i];
    }
    //dp[i]背包容量为i的最大价值
    //需要注意的是一维度需要逆序更新,因为正序更新
    //对于第i个,可能由i-2转移,却还是使用的i-1转移,所以逆序
    for (int i = 1; i <= n; ++i) {      //枚举物品
        for (int j = m; j >= v[i]; --j) {   //从大容积向小容积枚举,注意j小于v[i]就不必更新
            dp[j] = max(dp[j],dp[j - v[i]] + w[i]);     //更新最大价值
        }
    }
    cout<<dp[m];
}
(2)完全背包

题目:完全背包

这个问题与01背包的不同就在于一个物品可以多拿,所以我们需要加一层循环,来枚举拿物品的个数,但这种二维做法是TLE的!但是优化可以去掉k层循环

二维写法:

void solve(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i];
    }
    //dp[i][j]代表i个物品,背包容量为j的最大价值
    for (int i = 1; i <= n; ++i) {
        //枚举容量,最大容量为m
        for (int j = 0; j <= m; ++j) {   //注意与01背包的差别,01背包是倒序
            //优化可以去掉k层循环
            dp[i][j] = dp[i-1][j];
            if(j >= v[i]){
                dp[i][j] = max(dp[i][j],dp[i][j-v[i]] + w[i]);//注意与01背包的差别,01背包是与dp[i-1][j - v[i]] + w[i]比
            }
        }
    }
    cout<<dp[n][m];
}

一维写法(优化版):我们可以模仿01背包的一维

void solve(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i];
    }
    //dp[i]代表背包容量为i的最大价值
    for (int i = 1; i <= n; ++i) {
        //枚举容量,最大容量为m
        for (int j = v[i]; j <= m; ++j) {   //注意与01背包的差别,01背包是倒序
            dp[j] = max(dp[j],dp[j - v[i]] + w[i]); //注意与01背包区分
        }
    }
    cout<<dp[m];
}
(3)多重背包1

题目:多重背包

这个问题,我们可以转化为n个01背包来求解

做法:

int dp[N];
int a[N],b[N];
int n,m;
void solve(){   //注意数组的大小不要开小了
    int v,w,s;
    int t = 0;  //跟新数组下标,把n个物品全都放到一个数组中
    cin>>n>>m;
    while (n--){
        cin>>v>>w>>s;
        while (s--){
            a[++t] = v;
            b[t] = w;
        }   //拆开,把多重背包拆成一个背包;
    }
    for (int i = 1; i <= t; ++i) {  //01背包模板
        for (int j = m; j >= a[i]; --j) {
            dp[j] = max(dp[j],dp[j-a[i]] + b[i]);
        }
    }
    cout<<dp[m];
}
(4)多重背包2

题目:多重背包

数据范围大的话,二进制优化:

int dp[N/2];
int v[N],w[N];
int n,m;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m;
    int cnt = 0;    //分组的类别
    for (int i = 1; i <= n; ++i) {
        int a,b,s;
        cin>>a>>b>>s;
        int k = 1;  //每个组的个数
        while (k <= s){
            cnt++;
            v[cnt] = a*k;   //整体体积
            w[cnt] = b*k;   //整体价值
            s -= k; //s要减小
            k *= 2;
        }
        if(s > 0){
            cnt ++;
            v[cnt]  = a*s;
            w[cnt] = b*s;
        }
    }
    n = cnt;
    //01背包模板
    for (int i = 1; i <= n; ++i) {
        for (int j = m; j >= v[i]; --j) {
            dp[j] = max(dp[j],dp[j-v[i]] + w[i]);
        }
    }
    cout<<dp[m]<<endl;
}
(5)多重背包3

题目:多重背包

int dp[M];
int v[N],w[N],s[N];
int g[M],q[M];
int n,m;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i]>>s[i];
    }
    for (int i = 1; i <= n; ++i) {
        memcpy(g,dp,sizeof g);
        for (int r = 0; r < v[i]; ++r) {
            int hh = 0,tt = -1;
            for (int j = r; j <= m; j += v[i]) {
                while (hh <= tt && j - q[hh] > s[i]*v[i]) hh++;
                while (hh <= tt && g[q[tt]] + (j - q[tt])/v[i]*w[i] <= g[j])--tt;
                q[++tt] = j;
                dp[j] = g[q[hh]] + (j - q[hh]) / v[i] * w[i];
            }
        }
    }
    cout<<dp[m]<<endl;
}

(6)混合三种背包问题

题目:混合背包

有的物品只可取一次,有的物品可取有限次,有的物品可取无限次。

对于这个问题,我们采取把01背包和完全背包转化为分组背包,在使用分组背包的二进制优化做法(转化为01背包)

int dp[N];
int v[N],w[N];
int n,m;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m;
    /**
     * 对于范围小的,我们采用转化法,将01背包和完全背包转化为多重背包
     */
     int cnt = 1;
    for (int i = 1; i <= n; ++i) {
        int a,b,s;
        cin>>a>>b>>s;
        if(s == -1){
            s = 1;
        } else if(s == 0){
            s = m/a;    //若为完全背包,则在最优情况下,只能取总体积/该物品体积向下取整
        }
        int k = 1;  //计算当前物品分为0,2,4,6.....
        while (k <= s){ //多重背包转化为01背包,二进制优化转化过程
            v[cnt] = a*k;
            w[cnt] = b*k;
            s -= k;
            k *= 2;
            cnt++;
        }
        if(s > 0){  //计算剩下的一部分
            v[cnt] = a*s;
            w[cnt] = b*s;
            cnt++;
        }
    }
    //01背包模版
    for (int i = 1; i <= cnt; ++i) {
        for (int j = m; j >= v[i]; --j) {
            dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
        }
    }
    cout<<dp[m]<<endl;
}
(7)分组背包问题

题目:分组背包

这道题和01背包差不多,直接写优化后的代码

做法:

int dp[N*2];
int v[N][N],w[N][N],s[N];
int n,m;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>s[i];
        for (int j = 1; j <= s[i]; ++j) {
            cin>>v[i][j]>>w[i][j];
        }
    }
    //模仿01背包的写法;
/*    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= m; ++j) {  //从大体积向小体积枚举
            dp[i][j] = dp[i-1][j];
            for (int k = 1; k <= s[i]; ++k) {
                if(j >= v[i][k]) dp[i][j] = max(dp[i][j],dp[i-1][j-v[i][k]] + w[i][k]);
            }
        }
    }*/
    for (int i = 1; i <= n; ++i) {
        for (int j = m; j >= 0; --j) {  //从大体积向小体积枚举
            for (int k = 1; k <= s[i]; ++k) {
                if(j >= v[i][k]) dp[j] = max(dp[j],dp[j-v[i][k]] + w[i][k]);
            }
        }
    }
    cout<<dp[m]<<endl;
}
(8)二维费用的背包问题

题目:二维费用的背包问题

这个题目与01背包的区别就在于又多了一个限制条件,多了一个总重量不超过背包可承受的最大重量。

int dp[N][N];        //dp[i][j]代表容量为i,重量为j的最大价值
int v[N],w[N],x[N];
int n,m,z;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m>>z;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>x[i]>>w[i];
    }
    //类似01背包,在循环里面再多加一层循环
    for (int i = 1; i <= n; ++i) {
        for (int j = m; j >= v[i]; --j) {
            for (int k = z; k >= x[i]; --k) {
                dp[j][k] = max(dp[j][k],dp[j - v[i]][k - x[i]] + w[i]);
            }
        }
    }
    cout<<dp[m][z]<<endl;
}
(9)背包问题求方案数

题目:背包问题求方案数

就是求01背包问题最优方案有多少种方案

int dp[N],cnt[N];        //dp[i]代表容量为i的最大价值,cnt[i]代表容积不超过i的最大方案数
int v[N],w[N];
int n,m;
void solve(){   //注意数组的大小不要开小了
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i]>>w[i];
    }
    //先初始化,初始什么也不装为1;
    for (int i = 0; i <= m; ++i) {
        cnt[i] = 1;
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = m; j >= v[i]; --j) {
            int value = dp[j-v[i]] + w[i];
            if(value > dp[j]){  //如果转移的话,cnt也跟着转移
                dp[j] = value;
                cnt[j] = cnt[j - v[i]];
            }else if(value == dp[j]){   //相等的话,cnt[j] += cnt[j-v[i]]
                cnt[j] = (cnt[j] + cnt[j-v[i]])%mod;
            }
        }
    }
    cout<<cnt[m]<<endl;
}
  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值