一 概述
背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。
二 动态规划算法分析
1 红色箭头是计算顺序。
从最后一行,依次计算到第一行。
对于每一行,依次从第一列计算到最后一列。
2 橙色箭头是回溯顺序。
三 C++代码
// knapsack-problem.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 0-1背包问题:动态规划算法。
#include <iostream>
using namespace std;
// 物品数量
const int N = 4;
void Knapsack(int v[], int w[], int c, int n, int m[][10]);
void Traceback(int m[][10], int w[], int c, int n, int x[]);
int main()
{
// 背包容量
int c = 8;
// 价值数组
int v[] = { 0,2,1,4,3 };
// 重量数组
int w[] = { 0,1,4,2,3 };
// 放置物品数组
int x[N + 1];
// 最大价值数组
int m[10][10];
cout << "待装物品重量分别为:" << endl;
for (int i = 1; i <= N; i++)
{
cout << w[i] << " ";
}
cout << endl;
cout << "待装物品价值分别为:" << endl;
for (int i = 1; i <= N; i++)
{
cout << v[i] << " ";
}
cout << endl;
Knapsack(v, w, c, N, m);
cout << "背包能装的最大价值为:" << m[1][c] << endl;
Traceback(m, w, c, N, x);
cout << "背包装下的物品编号为:" << endl;
for (int i = 1; i <= N; i++)
{
if (x[i] == 1)
{
cout << i << " ";
}
}
cout << endl;
return 0;
}
// 计算最大价值
void Knapsack(int v[], int w[], int c, int n, int m[][10])
{
// 背包容量阈值
int jMax = min(w[n] - 1, c);
// 第一步:填写表格最后一行
// 背包容量小于等于阈值:不能将最后一个物品放入背包,最大价值是0。
for (int j = 0; j <= jMax; j++)
{
m[n][j] = 0;
}
// 背包容量大于阈值:可以将最后一个物品放入背包,最大价值就是最后一个物品的价值。
for (int j = w[n]; j <= c; j++)
{
m[n][j] = v[n];
}
//第二步:填写表格中间的行
for (int i = n - 1; i > 1; i--)
{
// 重新计算阈值
jMax = min(w[i] - 1, c);
// 小于等于阈值,使用下一行该列的值
for (int j = 0; j <= jMax; j++)
{
m[i][j] = m[i + 1][j];
}
// 大于阈值,取较大值:可能不放入本物品,也可能放入本物品。
for (int j = w[i]; j <= c; j++)
{
m[i][j] = max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);
}
}
// 第三步:填写表格第一行
m[1][c] = m[2][c];
if (c >= w[1])
{
m[1][c] = max(m[1][c], m[2][c - w[1]] + v[1]);
}
}
// 回溯
void Traceback(int m[][10], int w[], int c, int n, int x[])
{
for (int i = 1; i < n; i++)
{
if (m[i][c] == m[i + 1][c])
{
x[i] = 0;
}
else
{
x[i] = 1;
// 重新计算剩余容量
c -= w[i];
}
}
// 是否放入最后一个物品
x[n] = (m[n][c]) ? 1 : 0;
}
四 计算复杂性分析
背包(Knapsack)的时间复杂度O(n*c),回溯(Traceback)的时间复杂度O(n)。