4.13学习-背包问题

本文探讨了后序DP在背包问题中的应用,包括1.01背包(物品选择)、完全背包(数量不限)、多重背包(数量受限)和混合背包(种类混合),并介绍了优化空间复杂度的方法,如滚动数组和二进制优化。通过实例代码演示了如何用单调队列解决特定情况下的最值问题。
摘要由CSDN通过智能技术生成

序列dp:后一个数和前面的数存在关系
背包问题:从某些数里选出一些数,组合dp。
当背包问题优化为一维的时候,只有完全背包问题的体积是从小到大枚举的,其余都是从大到小循环。是两维的话没有任何限制。

for 物品:
    for 体积:
        for 决策

1.01背包问题:每个物品选、不选
在这里插入图片描述
2.完全背包问题:每个物品可以选0~无穷个
求所有前缀的最大值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.多重背包问题:每个物品可以选0~s[i]个
枚举一下最后一个物品选多少个
求滑动窗口内的最大值在这里插入图片描述
r = j%v
在这里插入图片描述
如果给定了某种物品只能取s个,那么相当于有一个大小为s的窗口内的最值。可以用单调队列求解。

4.混合背包问题
每个种类的物品有1个,多个,无限个

5.分组背包问题
每组物品有若干个,同一组内的物品最多只能选一个。

例题:
1.01背包问题
朴素版:f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]
优化:
这里对空间复杂度进行优化:
观察上面等式可以发现:更新f[i]层的时候,只用到了上一层的状态,因此可以用滚动数组进行优化。更新j的时候,用到的是上一层的j-v[i],因此可以从大到小枚举j,f[j] = max(f[j],f[j-v[i]]+w[i],这里,按照从大到小进行枚举的话就是j-v[i]是上一层的值。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int w[N],v[N],f[N];
int n,k;
int main()
{
    cin>>n>>k;
    for (int i = 0; i < n; i ++ ) cin>>v[i]>>w[i];
    for (int i = 0; i < n; i ++ )
    {
        for(int j = k;j >= v[i];j--)
        {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout << f[k] << endl;
    return 0;
}

2、完全背包问题
朴素版:f[i][j] = max(f[i-1][j],f[i][j-v]+w)
用滚动数组优化,从小到大枚举每个体积,保证j之前j-v已经更新

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N],w[N],v[N];
int n,m;
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++)
        {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

3、多重背包问题I

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int n,m;
int v[N],w[N],s[N];
int f[N][N];
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 ++ )
        {
            f[i][j] = max(f[i][j],f[i-1][j]);
            for (int k = 1; k <= s[i] && k*v[i]<=j; k ++ )
            {
                f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

4、多重背包问题II
按照刚刚的来算的话,时间复杂度为O(NVS),使用二进制优化,将每个s[i]利用1 2 4 8…c表示s[i],使得时间复杂度降低到logs级别,然后利用01背包问题即可求解

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25000,M=2010;
int f[N],v[N],w[N];
int n,m;
int main()
{
    cin>>n>>m;
    int cnt = 0; // 统计实际有多少01背包问题
    for (int i = 1; i <= n; i ++ )
    {
        int a,b,s;
        cin>>a>>b>>s;
        int k = 1;
        while(k <= s)
        {
            cnt++;
            v[cnt] = k*a;
            w[cnt] = k*b;
            s-=k;
            k*=2;
        }
        if(s)
        {
            cnt++;
            v[cnt] = s*a;
            w[cnt] = s*b;
        }
    }
    
    n = cnt;
    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;
}

5、混合背包问题
01背包看成特殊的多重背包,才用二进制优化。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N],n,m;
int main()
{
    cin>>n>>m;
    for (int i = 1; i <= n; i ++ )
    {
        int v,w,s;
        cin>>v>>w>>s;
        if(s == 0)
        {
            for(int j=v;j<=m;j++) f[j] = max(f[j],f[j-v]+w);
        }
        else
        {
            // 对于01背包是特殊的多重背包
            if(s == -1) s = 1;
            
            for(int k=1;k<=s;k*=2)
            {
                for(int j=m;j>=k*v;j--) f[j] = max(f[j],f[j-k*v]+k*w);
                s-=k;
            }
            if(s)
            {
                for(int j=m;j>=s*v;j--) f[j] = max(f[j],f[j-s*v]+s*w);
            }
        }
        
    }
    cout<<f[m]<<endl;
    return 0;
}

6、分组背包问题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int n,m;
int f[N],v[N][N],w[N][N],s[N];
int main()
{
    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];
        }
    }
    
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = m; j >= 0; j -- )
        {
            for(int k = 0; k <= s[i]; k ++)
            {
                if(j >= v[i][k]) f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值