01背包:有N件物品和一个容量为V的背包,每种物品均只有一件。第i件物品的费用是c[i],价值为V[i]。求解将哪些物品装入装入背包可使价值总和最大
完全背包:有N种物品和一个容量为V的背包,每种物品都可以无限件的使用,第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包的容量,且价值总和最大。
多重背包:有N种物品和一个容量为v的背包,第i种物品最多有n[i]件可以使用,每件费用是c[i],价值是w[i],求解将哪些物品装入背包上可使这些物品的费用总格不超过背包容量,且价值总和最大
0-1背包中:每种物品选择放或者不放,用字问题定义状态,即f[i][j]表示前i件物品恰放入一个容量为V的背包可以获得的最大价值
F[i][j] = max{f[i - 1][v] , f[i - 1][v - c[i]] + w[i]};
把这个过程理解下:
在前i件物品放进容量v的背包时,它有两种情况
情况一: 第i件不放进去,这时所得价值为:f[i-1][v]
情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]
(第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品)
最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。 (这里是重点,理解!)
#include<stdio.h>#include <cstring>int max(int a,int b){
int max;
max = a >= b ? a : b;
return max;
}int m[1005][1005];int main(){
int n;
int a,b;
int w[1005],v[1005];
while(~scanf("%d",&n)){
while(n--){
scanf("%d %d",&a,&b); // 5 ,10
memset(m,0,sizeof(m));
for(int i = 0 ; i < a ; i++){
scanf("%d",&w[i]);
}
for(int j = 0 ; j < a ; j++){
scanf("%d",&v[j]);
}
for(int i = 0 ; i < a ; i++){
for(int j = 0 ; j <= b ; j++){
if(j < v[i]){
m[i + 1][j] = m[i][j];
}
else
m[i + 1][j] = max(m[i][j] , m[i][j - v[i]] + w[i]);
}
}
printf("%d\n",m[a][b]);
}
}
return 0;
}
化为一维数组优化后
#include <iostream>
#include<cstring>
using namespace std;
int max(int a,int b){
int max = 0;
max = a >= b ? a : b;
return max;
}
int main()
{
int nCases;
int nPack, nMaxVolume;
int weight[1002], value[1002];
int record[1002];
scanf("%d", &nCases);
while(nCases--)
{
memset(record, 0, sizeof(record));
scanf("%d %d", &nPack, &nMaxVolume);
for(int i=0; i<nPack; ++i)
scanf("%d", &value[i]);
for(int i=0; i<nPack; ++i)
scanf("%d", &weight[i]);
for(int i=0; i<nPack; ++i)
for(int j=nMaxVolume; j>=weight[i]; --j)
{
record[j] =max(record[j], record[j-weight[i]]+value[i]); //每i++后更新一下在最后一个位置的最大值即可
}
printf("%d\n", record[nMaxVolume]);
}
return 0;
}
完全背包
想必大家看出了和01背包的区别,这里的内循环是顺序的,而01背包是逆序的。
现在关键的是考虑:为何完全背包可以这么写?
在次我们先来回忆下,01背包逆序的原因?是为了是max中的两项是前一状态值,这就对了。 那么这里,我们顺序写,这里的max中的两项当然就是当前状态的值了,为何? 因为每种背包都是无限的。当我们把i从1到N循环时,f[v]表示容量为v在前i种背包时所得的价值,这里我们要添加的不是前一个背包,而是当前背包。所以我们要考虑的当然是当前状态。
完全背包的代码与0-1背包的代码基本相同,只是第二个for中开始的位置不同而已
for(int i=0; i<nPack; ++i)
for(int j=weight[i]; j<=nVolume; ++j)
if(record[j]>record[j-weight[i]]+value[i])
record[j] = record[j-weight[i]]+value[i];
多重背包
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]
表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}
这里同样转换为01背包:
普通的转换对于数量较多时,则可能会超时,可以转换成二进制(暂时不了解,所以先不讲) 对于普通的。就是多了一个中间的循环,把j=0~bag[i],表示把第i中背包从取0件枚举到取bag[i]件。
#include <iostream>using namespace std;
int nCases;int nValue, nKind;int value[105], weight[105], bag[105];int nMultiplePack[105];
int main(){
//freopen("input.txt", "r", stdin);
scanf("%d", &nCases);
while(nCases--)
{
memset(nMultiplePack, 0, sizeof(nMultiplePack));
scanf("%d %d", &nValue, &nKind);
for(int i=0; i<nKind; ++i)
scanf("%d %d %d", &value[i], &weight[i], &bag[i]);
for(int i=0; i<nKind; ++i)
for(int j=0; j<bag[i]; ++j)
for(int k=nValue; k>=value[i]; --k)
if(nMultiplePack[k] < nMultiplePack[k-value[i]]+weight[i])
nMultiplePack[k] = nMultiplePack[k-value[i]] + weight[i];
printf("%d\n", nMultiplePack[nValue]);
}
return 0;}
01背包,完全背包,多重背包
最新推荐文章于 2023-08-24 20:45:16 发布