一、 单背包问题
-
解法归纳:
-
一、如果装不下当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一
样的。 -
二、如果装得下当前物品。
-
假设1:装当前物品,在给当前物品预留了相应空间的情况下,前n-1个物品的最佳组合加上当前物品的价值就是总价值。
-
假设2:不装当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
-
选取假设1和假设2中较大的价值,为当前最佳组合的价值。
-
-
-
得到最大价值
//动态规划找到容量为r的背包所能装下的最大价值,n个物品
int maxValue(int n, int r) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= r; j++) {
if (weight[i] > j)
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
return dp[n][r];
}
//回溯找到背包里的物品是什么,i为编号,j为容量,n为物品数量,在object[]里存放
void fid(int i, int j, int n) {
if (i == 0) {
for (int k = 1; k <= n; k++)
if (object[k] == 1)
printf("%d ", k);
return;
}
if (dp[i][j] == dp[i - 1][j]) {
object[i] = 0;
fid(i - 1, j, n);
} else if (dp[i][j] == max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])) {
object[i] = 1;
fid(i - 1, j - weight[i], n);
}
}
const int maxn = 1e5 + 6;
//weight-->weight,value-->value,dp[编号][容量]-->前n个编号的在容量之内的最优价值
int weight[maxn], value[maxn], dp[10000][10000];
int object[maxn];
//动态规划找到容量为r的背包所能装下的最大价值,n个物品
int maxValue(int n, int r) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= r; j++) {
if (weight[i] > j)
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
return dp[n][r];
}
//回溯找到背包里的物品是什么,i为编号,j为容量,n为物品数量,在object[]里存放
void fid(int i, int j, int n) {
if (i == 0) {
for (int k = 1; k <= n; k++)
if (object[k] == 1)
printf("%d ", k);
return;
}
if (dp[i][j] == dp[i - 1][j]) {
object[i] = 0;
fid(i - 1, j, n);
} else if (dp[i][j] == max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])) {
object[i] = 1;
fid(i - 1, j - weight[i], n);
}
}
int main() {
int n, r, w, v;
scc(n, r);
for (int i = 1; i <= n; i++) {
scc(w, v);
weight[i] = w;
value[i] = v;
}
p(maxValue(n, r));
fid(n, r, n);
}
二、 多重背包问题
问题概述:
背包容量bag,有num[i]个价值为value[i]、重量为weight[i]的物品,求最高价值
代码:
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include <vector>
#include <cctype>
#include <cstdlib>
using namespace std;
#define ll long long
#define il inline
#define oo 2147000000
#define sc(x) scanf("%d",&x)
#define scc(x, y) scanf("%d%d",&x,&y)
#define sccc(x, y, z) scanf("%d%d%d",&x,&y,&z)
#define p(x) printf("%d\n",x)
#define m(x, y) (x+y)>>1
#define l(x) x<<1
#define r(x) x<<1|1
#define MAX 1000000
int dp[MAX];//存储最后背包最大能存多少
int value[MAX], weight[MAX], num[MAX];//分别存的是物品的价值,每一个的重量以及数量
int bag;
void zeroOnePack(int weight, int value) {//01背包
int i;
for (i = bag; i >= weight; i--) {
dp[i] = max(dp[i], dp[i - weight] + value);
}
}
void completePack(int weight, int value) {//完全背包
int i;
for (i = weight; i <= bag; i++) {
dp[i] = max(dp[i], dp[i - weight] + value);
}
}
void multiplePack(int weight, int value, int num) {//多重背包
if (bag <= num * weight) {//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
completePack(weight, value);
return;
} else {//否则就将多重背包转化为01背包
int k = 1;
while (k <= num) {
zeroOnePack(k * weight, k * value);
num = num - k;
k = 2 * k;//这里采用二进制思想
}
zeroOnePack(num * weight, num * value);
}
}
int main() {
int n;
while (~scc(bag, n)) {
int i, sum = 0;
for (i = 0; i < n; i++) {
scc(num[i],value[i]);//此题没有weight,可以认为weight=value
}
memset(dp, 0, sizeof(dp));
for (i = 0; i < n; i++) {
//调用多重背包,注意传参的时候分别是重量,价值和数量
multiplePack(value[i], value[i], num[i]);
}
p(dp[bag]);
}
return 0;
}