01背包问题(动态规划)

问题描述:给定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

 

 

 

 

你可能感兴趣的:(算法设计与分析)