例题引入:
原题链接:https://www.acwing.com/problem/content/4/
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
简单介绍,这是多重背包模型的模板题,下面讨论如何解决;
直接思路:
这个跟完全背包很像,完全背包不限制每件物品取的次数,而多重背包规定了每次物品的次数,所以可以按照完全背包的写法来写。
状态表示: f[i][j]
集合:所有只考虑前i个物品,且总体积不超过j的所有选法;
属性:max;
状态计算:即集合的划分
这里就考虑第i个物品选几个,从0,1,2,3,…s[i];
代码实现:
朴素做法(n3):
#include<bits/stdc++.h>
using namespace std;
int v[110];//体积
int w[110];//价值
int s[110];//个数
int f[110][110];//前i个物品,总体积不超过j的最大价值
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&v[i],&w[i],&s[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];//这个删去也可
for(int k=0;k<=s[i];k++)
{
if(j>=k*v[i])//只有可以放得下的情况下才放
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
printf("%d",f[n][m]);
return 0;
}
优化成一维:
#include<bits/stdc++.h>
using namespace std;
int v[110];
int w[110];
int s[110];
int f[110];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&v[i],&w[i],&s[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)//优化成一维注意这里的倒序
{
for(int k=0;k<=s[i];k++)
{
if(j>=k*v[i])
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
//对比来看!:f[i][j]=max(f[i-1][j],f[i]f[k-k*v[i]]+k*w[i]);
}
}
}
printf("%d",f[m]);
return 0;
}
二进制优化(nvlogs)
这里介绍这个方法,当然这个方法是前人总结出来的,我们现在就是去理解然后掌握;
首先要考虑为什么要优化,因为数据量大的时候会超时;
其次,为什么不能按照完全背包的优化方式进行优化;
原因:
最后,为什么可以用二进制优化;
原因:
任何一个非负整数,可以用一个以1为首项,2为公比的等比数列中的数来表示,
例:10
可以用1,2,4,8,这几个数表示,
1:可以表示0~1;
1,2:表示0~3;
1,2,4:表示0~7;
1,2,4,8:表示0~15;
故可以表示10;
二进制如何优化的:
展现:那么这题如何优化呢,这题我们可以根据上方的推到,将其以方式分成若干组,logn组,但是这里我们只需且只可表示到不能大于其总数,即若表示10,那么一个分成,1,2,4,3,这四个组。
对一个数n,则分成1,2,4,…2k ,c(c<2k+1),并且应该:c+2k+1-1=n;
代码实现:
#include<bits/stdc++.h>
using namespace std;
int v[12000];
int w[12000];
int f[12000];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
int cnt=0;//注意cnt要定义此范围的全局
for(int i=1;i<=n;i++)
{
int a,b,s;
scanf("%d%d%d",&a,&b,&s);
int k=1;//以2的倍数增加
while(k<=s)
{
cnt++;
v[cnt] = a * k;//体积*个数
w[cnt] = b * k;//价值*个数
s -= k;//不断的减,直至s<k
k *= 2;
}
if(s>0)//将剩下的存入
{
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
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]);
}
}
printf("%d\n",f[m]);
return 0;
}
忘记说了,利用二进制优化之后,就是01背包了,因为,第i类物品个数被重新分组,分组以后,每个组可以选可以不选,全选就是这个组本来的数目,全不选那不就是0嘛,选其中几个等等,就会构成其原来0,1,2,3,…n;然后这个01背包是优化成一维的;