一、题目
有 N种物品和一个容量是 V的背包。
物品一共有三类:
第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si次(多重背包);
每种体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i种物品的体积、价值和数量si=−1
表示第 i种物品只能用1次;
si=0
表示第 i种物品可以用无限次;
si>0表示第 i
种物品可以使用 si次;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000
输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8
二、思路
思路参考acwing大佬的代码
首先将多重背包、01背包、完全背包都转化为多重背包处理,进行二进制优化
二进制优化就是指,将n个物品分成1,2,4...个一组,并将每组的体积和价值算出,这样就可以将这组看成一个物品,如果到第2的i次幂个一组不够分了,就将剩的物品放在一组
s<0 表示只能分成1个物品一组;s==0表示无穷多个物品,可是只有m容量,就算都放这个物品最多也只能放m/a个此物品
最后就回到了01背包问题 进行一维数组优化
三、java and c++代码
java
import java.io.*;
public class Main {
//定义数组存放新的分组(物品)体积 价值 在体积为i时物品价值f[i]
static int[] v = new int[100010];
static int[] w = new int[100010];
static int[] f = new int[100010];
public static void main(String[] args) throws Exception{
StreamTokenizer re = new StreamTokenizer(new InputStreamReader(System.in));
PrintWriter pt = new PrintWriter(System.out);
//新的分组物品下标
int cnt = 1;
//一共n个物品 背包容量为m
re.nextToken();int n = (int)re.nval;
re.nextToken();int m = (int)re.nval;
//循环输入每个物品的体积a 价值b s>0 表示有s个此物品 <0表示有1个该物品 ==0表示有无穷个该物品 无穷个的时候 最多能放s = m / a向下取整个物品
for(int i = 1;i <= n;i ++)
{
re.nextToken();int a = (int)re.nval;
re.nextToken();int b = (int)re.nval;
re.nextToken();int s = (int)re.nval;
//使用二进制优化 物品数量以2倍速增长 并更新物品体积数组 使得每个分组总体积成为一个新的物品
int k = 1;
if(s < 0) s = 1;//表示01背包
else if(s == 0) s = m / a;//表示完全背包 最多只能放m/a个这个物品
while(s >= k)
{
//说明还有物品足够分成k个一组
v[cnt] = k * a;
w[cnt] = k * b;
s -= k;
k *= 2;
cnt ++;
}
if(s > 0)
{
//不够分k个一组但是还剩下物品 将剩下的物品分成一组
v[cnt] = s * a;
w[cnt] = s * b;
s -= s;
cnt ++;
}
}
//此时变成01背包问题一维数组优化
for(int i = 1;i <= cnt;i ++)
{
for(int j = m;j >= v[i];j --)
{
f[j] = Math.max(f[j],f[j - v[i]] + w[i]);
}
}
pt.println(f[m]);
pt.close();
}
}
c++
#include <bits/stdc++.h>
using namespace std;
int n,m,v[100010],w[100010],f[100010];
int main()
{
cin>>n>>m;
int cnt = 1;
for(int i = 1;i <= n;i ++)
{
int a,b,s;
cin>>a>>b>>s;
int k = 1;
//表示第i件物品只能用一次
if(s < 0)
s = 1;
//表示第i件物品有无穷多个 最多只能放s个
else if(s == 0) s = m / a;
while(k <= s)
{
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
cnt ++;
}
if(s > 0)
{
v[cnt] = s * a;
w[cnt] = s * b;
cnt ++;
}
}
//将多重背包进行二进制优化
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]<<endl;
return 0;
}
四、总结
这两个代码逻辑相同,明显感受到c++语言输入输出的方便性,但是对于c++语言没有java语言熟练。刚开始看大佬思路一头雾水,用我的DEVC++打了两遍思路逐渐清晰,就转化为java代码,大佬的代码属实妙不可言,评论区全是666。