问题描述:给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包容量是c,问应如何选择装入背包中的中的物品,使得装入物品的总价值最大?
问题分析:我们用m[i][j]表示i~n的物品放入容量为j的背包里可以取得的最大价值,cw表示当前背包容量。在判断一个物品是否可以被放入时,若cw比wi小,说明wi放不下了,若cw比wi大,此时m[i][j] = m[i+1][j],那么有两种情况:(1):放入背包后恰好达到最大价值,此时m[i][j] = m[i+1][j];(2):不是最佳选择,后面还有更好的选择,此时需要做一次对比,对比的两项分别是:m[i+1][j]和m[i+1][j-wi](不包含i这个物品) + vi(物品i+1~n放入j-wi的最大值再加上vi),两者取最大值就好了。经过以上分析我们可以得到一个函数表达式 :
m[n-1][j] = {
m[n][j] j < w[n-1]
max( m[n][j], m[n][j-w[n-1]]+v[n-1] ) j >= w[n-1]
}
得到这个以后,如果我们知道m[n][j](j:0~n)我们就可以不断地向后推导得到m[1][c]的值,经过分析其实我们发现m[n][j]的之其实很好算,如果wn大于j就为0,小于就为vn,那么可以得到以下表达式:
m[n][j] = {
vn j >= wn
0 j < wn
}
根据函数式就可以写代码了:
#include
/*函数表达式:
m[n-1][j]:
m[n-1][j] = max( m[n][j], m[n][j-w[i]] + v[n-1] ) j >= w[n-1]
m[n-1][j] = m[n][j] j < w[n-1]
m[n][j]:
vn j >= w[n]
0 j < w[n]
*/
int m[100][100] ;
int max(int a, int b)
{
if(a > b){
return a ;
}
else
return b ;
}
int min(int a, int b)
{
if(a > b){
return b ;
}
else
return a ;
}
void knapsack(int v[], int w[], int c, int n)
/*函数功能:v[]和w[]数组下标从一开始,实现上面的函数表达式*/
{
int i, j ;
int jMax = min(w[n]-1, c) ; //此句程序也可以换成jMax = w[n]-1但数组可能会越界,这样更好
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[n]-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[1][c], m[2][c-w[1]] + v[1]) ;
}
}
void Traceback(int w, int c, int n, int x[])
/*计算物品的存放情况0表示不存,1表示存*/
{
int i ;
for(i=0; i
扩展:给背包加上容积(不考虑空隙)
#include
/*函数表达式:
m[n-1][j]:
m[n-1][j] = max( m[n][j], m[n][j-w[i]] + v[n-1] ) j >= w[n-1] && k >= b[n-1]
m[n-1][j] = m[n][j] j < w[n-1] || k < b[n-1]
m[n][j]:
vn j >= w[n] && k >= b[n]
0 j < w[n] || k < b[n]
*/
int m[100][100][100] ;
int max(int a, int b)
{
if(a > b){
return a ;
}
else
return b ;
}
int min(int a, int b)
{
if(a > b){
return b ;
}
else
return a ;
}
void knapsack(int v[], int w[], int b[], int c, int n, int t)
/*函数功能:v[]和w[]数组下标从一开始,实现上面的函数表达式*/
{
int i, j, k ;
int jMax = min(w[n]-1, c) ; //此句程序也可以换成jMax = w[n]-1但数组可能会越界,这样更好
int kMax = min(b[n]-1, t) ;
for(j=0; j<=jMax; j++)
for(k=0; k<=kMax; k++)
m[n][j][k] = 0 ;
for(j=w[n]; j<=c; j++)
for(k=b[n]; k<=t; k++)
m[n][j][k] = v[n] ;
for(i=n-1; i>1; i--){
jMax = min(w[n]-1, c) ;
for(j=0; j<=jMax; j++)
for(k=0; k<=kMax; k++)
m[i][j][k] = m[i+1][j][k] ;
for(j=w[i]; j<=c; j++)
for(k=b[n]; k<=t; k++)
m[i][j][k] = max(m[i+1][j][k], m[i+1][j-w[i]][k-b[i]] + v[i]) ;
}
m[1][c][t] = m[2][c][t] ;
if(c >= w[1] && t >= b[1]){
m[1][c][t] = max(m[1][c][t], m[2][c-w[1]][t-b[1]] + v[1]) ;
}
}
void Traceback(int w[], int b[], int c, int n, int t, int x[])
/*计算物品的存放情况0表示不存,1表示存*/
{
int i ;
for(i=0; i