有n个物品,每个物品重量为wi, 价值为pi, 有装载重量为c的箱子,问如何进行装载是箱子中物品的总价值最大
sum(pi xi), xi为0或者1,0表示不装载此货物,1表示装载次货物,约束条件为sum(wi xi)<=c
复杂度:NP
1. 贪婪的方法是不不能保证最优的
一般有如下三种贪婪方式,a)最大价值 b)最小重量 c)价值重量比 pi/wi
2.递归解法
假设f(i, j)表示1,2,i-1个物品已经选择,剩余载重为j的最优解
int recursiveKnapsack(int i, int y)
{
if(i==n-1)return (y
if(y
return max(recursiveKnapsack(i+1,y), recursiveKnapsack(i+1,y-w[i])+p[i]);
}
这种解法的复杂度很高为2^n, 其实这种方法等同于枚举位长为n的二进制串的所有组合。
3 动态规划方法
此处的想法其实和递归是一样的,只是这里用空间换取时间上的高效了。
设f(i,y)为剩余载重为y,剩余物品为i,i+1,...,n的最优解
则f(i,y)=max(f(i+1,y), f(i+1, y-wi)+pi) if y>=wi
else f(i,y)=f(i+1,y)
写成程序为
void
DPKnapsack(int dp[][c+1])
{
for (int y = 0; y < w[n-1]; y++)
dp[n-1][y] = 0;
for (int y = w[n-1]; y <= c; y++)
dp[n-1][y] = p[n-1];
for (int i = n - 2; i > 0; i--)
{
for (int y = 0; y < w[i]; y++)
dp[i][y] = dp[i + 1][y];
for (int y = w[i]; y <= c; y++)
dp[i][y] = max(dp[i + 1][y], dp[i + 1][y - w[i]] + p[i]);
}
dp[0][c] = dp[1][c];
if (c >= w[1])
dp[0][c] = max(dp[1][c], dp[1][c - w[1]] + p[0]);
}
注意程序中和表述中的下标差一问题。
此处我们假设问题数据为全局的,比如:
const int n = 3;
const int w[] =
{ 100, 10, 10 };
const int p[] =
{ 20, 15, 15 };
const int c = 105;
这个问题的解为[0 1 1], 最大价值为30.
3. 回溯
backtrackKnapsack(int i, int c, int sump, int& max)
{
if (i >= n)
{
if (sump > max)
max = sump;
return;
}
if (c >= w[i])
backtrackKnapsack(i + 1, c - w[i], sump + p[i], max);
backtrackKnapsack(i + 1, c, sump, max);
}