一:简单01背包
代码如下:
#include<iostream>
#include <functional>
using namespace std;
const int itemnum = 25;//物品数
const int valunum = 2005;//最大容量
int item[itemnum];
int volu[valunum];
int dp[itemnum][valunum];
int main()
{
int V = 0, N = 0;//V是体积容量,N是物品数
scanf("%d %d", &V, &N);
//物品的体积
for (int i = 1; i <= N; i++)
{
scanf("%d", &item[i]);
}
//将第一行第一列初始化为0
for (int i = 0; i <= V; i++)
dp[0][i] = 0;
for (int i = 0; i <= N; i++)
dp[0][i] = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= V; j++)
{
if (item[i] > j)
dp[i][j] = dp[i - 1][j];//不放该物品
else //如果当前物品体积小于等于此时最大体积容量,可以装下的最大容量为不放该物品和放该物品容量最大值
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - item[i]] + item[i]);
}
}
printf("%d\n", V - dp[N][V]);
system("pause");
return 0;
}
二:价值背包
注:可以把物品的费用理解为物品的体积。
例1:小米买了一个最大容量为N的行李箱去旅游,他想带很多物品,已经知道了每个物品体积大小 V 和的重要度 W ,小米想要带上的物品总重要度最高,你能帮帮他吗?(每个物品都只有一件,且行李箱子可以不装满。)
代码如下:
#include<iostream>
#include <functional>
using namespace std;
const int maxN = 105;
const int maxM = 105;
int main()
{
int M, N;
int vum[maxM] = { 0 };
int value[maxM] = { 0 };
int dp[maxM][maxN];
while (scanf("%d %d", &N, &M) != EOF)
{
for (int i = 1; i <=M; i++)
scanf("%d", &vum[i]); //每个物品体积
for (int i = 1; i <= M; i++)
scanf("%d", &value[i]);//每个物品价值
//第一行初始化为0
for (int j = 0; j <= N; j++)
{
dp[0][j] = 0;
}
//第一列初始化为0
for (int i = 0; i <= M; i++)
{
dp[i][0] = 0;
}
//N是容量 M是数量
for (int i = 1; i <=M; i++)
{
for (int j = 1; j <= N; j++)
{
if (vum[i] > j)
{
dp[i][j] = dp[i - 1][j];
}
else
dp[i][j] = max(dp[i-1 ][j], dp[i-1 ][j - vum[i]]+value[i]);
}
}
printf("%d\n", dp[M][N]);
}
system("pause");
return 0;
}
三:完全背包
完全背包和01价值背包问题的区别在于每一件物品的数量都有无限个,而01背包每件物品数量只有一个。
在递推公式时,需要加以改变:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]); 注意这里需要考虑放入一个物品i时还可能继续放入i,所以不能是dp[i][j] = max(dp[i - 1][j], dp[i-1][j - weight[i]] + value[i]);
#include<iostream>
#include <functional>
using namespace std;
const int maxN = 105;
const int maxM = 105;
int main()
{
int M, N;
int weight[maxM] = { 0 };
int value[maxM] = { 0 };
int dp[maxM][maxN];
int num[maxM] = { 0 };//记录每个物品放多少
while (scanf("%d %d", &N, &M) != EOF)
{
for (int i = 1; i <= M; i++)
scanf("%d", &weight[i]); //每个物品体积
for (int i = 1; i <= M; i++)
scanf("%d", &value[i]);//每个物品价值
//第一行初始化为0
for (int j = 0; j <= N; j++)
{
dp[0][j] = 0;
}
//第一列初始化为0
for (int i = 0; i <= M; i++)
{
dp[i][0] = 0;
}
//N是容量 M是数量
//计算前i件物品恰放入一个容量为j的背包可以获得的最大价值
for (int i = 1; i <= M; i++)
{
for (int j = 1; j <= N; j++)
{
if (weight[i] <= j)
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]); //注意是dp[i][j-weight[i]],因为要考虑放入一个物品i时可能继续放入i
}
else
dp[i][j] = dp[i - 1][j];
}
}
//计算每个物品放入的个数
int j = N;
for (int i = M; i > 0; i--)
{
if (dp[i][j] == dp[i][j - weight[i]] + value[i])
{
num[i]++;
j = j - weight[i];
}
}
//打印前i件物品恰放入一个容量为j的背包可以获得的最大价值
for (int i = 1; i <= M; i++)
{
for (int j = 1; j <= N; j++)
printf("%d ", dp[i][j]);
printf("\n");
}
//M个物品放入容量为N的背包的最大价值
printf("%d\n",dp[M][N]);
//每个物品放入的个数
for (int i = 1; i <= M; i++)
{
printf("%d ", num[i]);
}
}
system("pause");
return 0;
}
四:多重背包
多重背包和01背包、完全背包的区别:多重背包中每种物品的数量是给定的,可能不是一个,绝对不是无限个。
方法一:转化为01背包。若有kind中物品,每种物品num个,将这些物品用01背包问题解决。
代码如下:
#include<iostream>
#include <functional>
const int maxN = 105;
const int maxM = 105;
int main()
{
int kind = 0, N = 0; //N是背包容量,kind是物品种类
int weight[maxM] = { 0 };
int value[maxM] = { 0 };
int dp[maxM][maxN];
scanf("%d %d", &kind,&N);
int countnum = 1;//一个的物品数
for (int i = 0; i < kind; i++)
{
int num;//每种物品有num件,
scanf("%d", &num);
int weight1, value1;
scanf("%d %d", &weight1, &value1);
while (num--)
{
weight[countnum] = weight1;
value[countnum] = value1;
//输入每种物品的每件物品的体积和价值
countnum++;
}
}
//第一行初始化为0
for (int i = 0; i <= N; i++)
dp[0][i] = 0;
//第一列初始化为0
for (int i = 0; i < countnum; i++)
dp[i][0] = 0;
//转化为01背包
for (int i = 1; i < countnum; i++)
{
for (int j = 1; j <= N; j++)
{
if (weight[i] <= j)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
else
dp[i][j] = dp[i - 1][j];
}
}
printf("%d", dp[countnum - 1][N]);
system("pause");
return 0;
}
方法二:
计算考虑kind种物品承重限制为N时最大价值f[kind][N]时,递推公式考虑两种情况,要么第 i 种物品一件也不放,就是f[i-1][j], 要么第 i 种物品放 k 件,其中 1 <= k <= (N/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][j]的值,即f[i][j] = max{f[i-1][j], (f[i-1][j-k*weight[i]]+k*value[i])}。 这里为什么不能像完全背包一样直接考虑f[i][j-weight[i]]+value[i]呢?因为这样不容易判断第 i 种物品的个数是否超过限制数量 num[i]。
#include<iostream>
#include<iostream>
using namespace std;
const int maxN = 105;
const int maxM = 105;
int main()
{
int kind = 0, N = 0; //N是背包容量,kind是物品种类
int weight[maxM] = { 0 };
int value[maxM] = { 0 };
int dp[maxM][maxN];
int num[maxM];
scanf("%d %d", &kind, &N);
for (int i = 1; i <= kind; i++)
{
scanf("%d", &num[i]);//每种物品的数量
scanf("%d %d", &weight[i], &value[i]);//每种物品的体积和价值
}
//第一行初始化为0
for (int i = 0; i <= N; i++)
dp[0][i] = 0;
//第一列初始化为0
for (int i = 0; i <= kind; i++)
dp[i][0] = 0;
for (int i = 1; i <= kind; i++)
{
for (int j = 1; j <= N; j++)
{
if (weight[i] <= j)
{
int count = min(num[i], j / weight[i]);//取种该物品件数和此时容量允许容纳该物品件数最小值
for (int k = 1; k <= count; k++)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - k*weight[i]] + k*value[i]);//注意是dp[i - 1][j - k*weight[i]]
}
else
dp[i][j] = dp[i - 1][j];
}
}
printf("%d\n", dp[kind][N]);
system("pause");
return 0;
}