多重背包问题 Ⅰ&Ⅱ &Ⅲ

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输出最大价值。

输入

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出

输出一个整数,表示最大价值。

Input

4 5

1 2 3

2 4 1

3 4 3

4 5 2

Output

10

根据不同的数据范围,有不同的选择

Ⅰ. 数据范围 0<N,V≤100 0<vi,wi,si≤100

直接写

复制代码
//代码一
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=510,M=6010;
int n,m;
int v,w,s;
int f[N][M];
void solve()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        cin>>v>>w>>s;
        for (int j=1;j<=m;j++)
        for (int k=0;k<=s&&k*v<=j;k++)
        f[i][j]=max(f[i][j],f[i-1][j-k*v]+k*w);
    }
    cout<<f[n][m];
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}


//代码二:降一维
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=510,M=6010;
int n,m;
int v,w,s;
int f[M];
void solve()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        cin>>v>>w>>s;
        for (int j=m;j>=v;j--)
        for (int k=0;k<=s&&k*v<=j;k++)
        f[j]=max(f[j],f[j-k*v]+k*w);
    }
    cout<<f[m];
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}

Ⅱ. 数据范围 0<N≤1000,0<V≤2000,0<vi,wi,si≤2000

通过二进制将每种物品按照个数的不同重新打包,再按01背包的方式选择

复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=1e6+10;
int n,m;
int f[N],v[N],w[N];
int cnt;
void solve()
{
    cin>>n>>m;
    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>0)
        {
            cnt++;
            v[cnt]=s*a;
            w[cnt]=s*b;
        }
    }
    for (int i=1;i<=cnt;i++)
    for (int j=m;j>=v[i];j--)
    f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m];
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}

Ⅲ. 数据范围 0<N≤1000,0<V≤20000,0<vi,wi,si≤20000

通过单调队列来更新状态

复制代码
//代码一
#include <bits/stdc++.h>
using namespace std;
//#define int long long         //不开就不开吧
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=1010,M=2e4+10;
int n,m;
int f[N][M],q[M];
void solve()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        int v,w,s;
        cin>>v>>w>>s;
        for (int j=0;j<v;j++)
        {
            int hh=0,tt=-1;
            for (int k=j;k<=m;k +=v)
            {
                if (hh<=tt&&k-q[hh]>s*v) hh++;      //判断队头是否滑出窗口,队列中的v不能超过上限s个
                while (hh<=tt&&f[i-1][q[tt]]-(q[tt]-j)/v*w<=f[i-1][k]-(k-j)/v*w) tt--;
                q[++tt]=k;
                f[i][k]=f[i-1][q[hh]]+(k-q[hh])/v*w;
            }
        }
    }
    cout<<f[n][m];
}
int main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}


//代码二 降一维
#include <bits/stdc++.h>
using namespace std;
//#define int long long         //不开就不开吧
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=2e4+10;
int n,m;
int f[N],g[N],q[N];
void solve()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        int v,w,s;
        cin>>v>>w>>s;
        memcpy(g,f,sizeof f);       //滚动数组,备份上一次的状态
        for (int j=0;j<v;j++)
        {
            int hh=0,tt=-1;
            for (int k=j;k<=m;k +=v)
            {
                if (hh<=tt&&k-q[hh]>s*v) hh++;      //判断队头是否滑出窗口,队列中的v不能超过上限s个
                while (hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w) tt--;
                q[++tt]=k;
                f[k]=g[q[hh]]+(k-q[hh])/v*w;
            }
        }
    }
    cout<<f[m];
}
int main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}
相关推荐
散峰而望几秒前
【算法竞赛】C++函数详解:从定义、调用到高级用法
c语言·开发语言·数据结构·c++·算法·github
CoderCodingNo10 分钟前
【GESP】C++五级真题(贪心思想考点) luogu-B4071 [GESP202412 五级] 武器强化
开发语言·c++·算法
我有一些感想……16 分钟前
An abstract way to solve Luogu P1001
c++·算法·ai·洛谷·mlp
前端小L18 分钟前
双指针专题(三):去重的艺术——「三数之和」
javascript·算法·双指针与滑动窗口
智者知已应修善业1 小时前
【求等差数列个数/无序获取最大最小次大次小】2024-3-8
c语言·c++·经验分享·笔记·算法
LYFlied2 小时前
【每日算法】LeetCode 416. 分割等和子集(动态规划)
数据结构·算法·leetcode·职场和发展·动态规划
多米Domi0112 小时前
0x3f 第19天 javase黑马81-87 ,三更1-23 hot100子串
python·算法·leetcode·散列表
历程里程碑2 小时前
滑动窗口最大值:单调队列高效解法
数据结构·算法·leetcode
量子炒饭大师2 小时前
Cyber骇客的逻辑节点美学 ——【初阶数据结构与算法】二叉树
c语言·数据结构·c++·链表·排序算法
課代表3 小时前
从初等数学到高等数学
算法·微积分·函数·极限·导数·积分·方程