给定n个物品和一个背包。物品i的重量为wi,价值为vi,背包容量为c。问如何选择装入背包中的物品,使得装入背包的物品的价值最大?在装入背包时,每种物品i只有两种选择,装入或者不装入,既不能装入多次,也不能只装入一部分。
因此,此问题称为0-1背包问题.0-1背包问题是一个特殊的整数规划问题。
输入:物品的数目n,背包的容量c。各个物品的重量wi,各个物品的价值vi。
输出:装入背包的最大价值。各个物品是否装入背包,装入输出1,不装入输出0.
运行结果:
0-1背包问题的形式描述:
•问题的形式描述是:给定c>0,wi>0,vi>0,1≤i≤n,求n元0-1向量(x1, x2, …, xn),使得
背包问题的最优子结构性质:
•设(y1, y2, …, yn)是所给0-1背包问题的一个最优解,则(y2, …, yn)是下面对应问题的最优解:
n否则,设(z2, …, zn)是最优解,则(y1, z2, …, zn)是原问题的最优解,而(y1, y2, …, yn)不是最优解。矛盾.、
•设所给0-1背包问题的子问题
其最优解为m(i, j),即背包容量为j,可选择物品为i, i+1, …, n时0-1背包问题的最优解,则
0-1背包问题的递归算法:
•用二维数组m[i][j], 0≤j≤c, 存储m(i, j)的值。
•求解0-1背包问题就是在二维数组m中填入相应的值。
•而m[1][c]中的值就是该背包问题的解。
在二维数组m中最先填入的应该是哪些呢?
二维数组m中最先填入只能选择物品n的最优解m(n, j):
若0≤j<wn,m[n][j]=0;
若 j≥wn,m[n][j]=vn。
n然后从物品n–1到物品1逐个填入它们的最优解m(i, j):
n若0≤j<wi,m[i][j]= m[i+1][j];
n若j≥wi, m[i][j]=max{m[i+1][j], m[i+1][j–wi]+vi}
template <class Type>
void Knapsack(Type *v, int *w, int c, int n, Type m[][maxn])
{
int i, j, jMax;
jMax = min(w[n]-1, c);
for(j = 0; j <= jMax; j++)
m[n][j] = 0;
for(j = w[n]; j <= c; j++)
m[n][j] = v[n];
for(i = n-1; i > 1; i--)
{
jMax = min(w[i]-1, c);
for(j = 0; j <= jMax; j++)
m[i][j] = m[i+1][j];
for(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[2][c], m[2][c-w[1]] + v[1]);
}
算法Traceback计算相应的最优解:
若m[1][c]=m[2][c],则x1=0,否则x1=1.
当x1=0时,由m[2][c]继续构造最优解。当x1=1时,由m[2][c-w1]继续构造最优解。依次类推,可构造出相应的最优解(x1,x2,.....xn)
template <class Type>
void Traceback(Type m[][maxn], int *w, int c, int n, int *x)
{
int i;
for(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;
}
0-1背包问题算法的复杂性:
•从算法中可以看出,对每个物品i要填入m[i][1], m[i][2], …, m[i][c],n个物品共要填写nc个,因此整个算法的时间复杂性是O(nc)。
•此算法在背包容量c很大时所需的计算时间量很大。如c=2n,则算法的复杂性为O(n2n)。
•算法的不足:要求物品的重量wi是整数,而不能为实数。