多重背包问题

\(N\) 种物品和一个容量是 \(V\) 的背包。

\(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)

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

输入格式

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

接下来有 \(N\) 行,每行三个整数 \(v_i, w_i, s_i\),用空格隔开,分别表示第 \(i\) 种物品的体积、价值和数量。

输出格式

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

数据范围

\(0 \lt N \le 1000\)
\(0 \lt V \le 2000\)
\(0 \lt v_i, w_i, s_i \le 2000\)

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

代码实现

朴素版

#include <iostream>
#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 ++ )
            for (int k = 0; k <= s[i] && k * v[i] <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);

    cout << f[n][m] << endl;
    return 0;
}
//https://www.acwing.com/activity/content/code/content/57826/
//来源:AcWing

二进制优化版

#include <iostream>
#include <algorithm>

using namespace std;
/*二进制优化(下方举例说明):
①假定物品件数s=200
②二进制形式k[n]={1,2,4,8,16,32,64,73}  其所有值相加是<=200  可以凑出[0~200]的所有数
③除开最后一个数不是二进制形式,73=200-(1+2+4+8+16+31+64)
④其余数,若定64,其中64+[0~63]->[0~127]
⑤最后一个数73,其中73+[0~127]->[0~200] */

const int N=12010,M=2010;   //此处1000 * log2000 , 向上取整数N 得12000

int n,m;         //n表示物品总数,m表示背包容积
int v[N],w[N];   //v[i]表示第i件物品的体积,w[i]表示第i件物品的价值
int f[N];        //组合方案中的最大价值

int main()
{
    cin>>n>>m;  
    int cnt=0;   //用来存储所有新的物品

    for(int i=1;i<=n;i++)
    {
        int a,b,s;
        cin>>a>>b>>s;     //输入当前物品的体积,价值,个数
        int k=1;          //此处二进制优化,从1开始分(假定选择当前物品的总数为k)
        while(k<=s)
        {
            cnt++;        //编号增加
            v[cnt]=a*k;   //体积*物品总数
            w[cnt]=b*k;
            s-=k;         //从总个数s中减去k个
            k*=2;         //二进制,乘以二继续循环
        }
        if(s>0)  //若总个数s还有剩余
        {
            cnt++;        //编号增加
            v[cnt]=a*s;   //体积*剩余个数
            w[cnt]=b*s;
        }
    }

    n=cnt;       //将n更新为cnt

    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--)      //此处优化成一维,次数使用倒序,因为原f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k)
            f[j]=max(f[j],f[j-v[i]]+w[i]);     //其中v[i]和w[i]已经是选定个数k后更新的体积和价值

    cout<<f[m]<<endl;

    return 0;
}
//https://www.acwing.com/activity/content/code/content/57846/
//来源:AcWing

posted @ 2023-08-30 13:47  LongDz  阅读(20)  评论(0)    收藏  举报