dp 01背包 多重背包 完全背包 T4 有  种物品和一个容量是  的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 sisi 次(多重背包);

每种体积是 ,价值是 

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

输入格式

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

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

  •  表示第  种物品只能用 1 次;
  •  表示第  种物品可以用无限次;
  •  表示第  种物品可以使用  次;

输出格式

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

数据范围

输入样例

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

输出样例:

8

思路

f[i][j]:
	当前物品能选几个:
        一个:
        - 不选 f[i - 1][j]
        - 选 f[i - 1][j - v[i]] + w[i]
        无限次:
        - 不选 f[i - 1][j]
        - 选 f[i - 1][j - k * v[i]] + k * w[i](j >= k * v[i])
        s_i次:
        - 不选 f[i - 1][j]
        - 选 f[i - 1][j - k * v[i]] + k * w[i](k <= s_i && j >= k * v[i])

因为数据范围是1e3, 故朴素版多重背包是不能做的, 需要进行二进制或者滑动窗口优化, 这里就用二进制优化, 也可以直接优化为1维, 不过完全背包是正序其他两个是逆序。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10, M = 1e4 + 10;
int f[M];
int 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 
        {
            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;
}