第七章 背包问题——完全背包

本文深入探讨了背包问题的解决策略,从初始状态方程的建立,到优化数据结构的实现,再到进一步的深入优化,详细阐述了如何通过算法优化来提升效率。通过实例分析,展示了从二维到一维的简化过程,并最终达到了显著的性能提升。

题目:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。放入第i种物品的费用是Ci,价值是Wi,求解:将哪些物品装入背包,可使这些物品耗费的费用和不超过背包容量,且价值总和最大。
分析:
(一)建立状态方程
可以转化为01背包问题求解
dp[i][v]表示前i件种物品放入容量为v的背包的最大价值,则有:
dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},其中0<=k*C[i]<=v,状态方程的建立不难理解
(二)根据状态方程实现代码
有了状态方程,看出i与i-1有关,所以i从1到N遍历,v与v-k*C[i]有关,所以k从0到k*C[i]<=v遍历
#include <iostream>  
#include <algorithm>  
using namespace std;  
  
#define N 3//物品个数  
#define V 10//背包容量  
  
int C[N+1]={0,10,4,5};  //Ci表示第i件物品的费用   (i从1开始)  
int W[N+1]={0,2,2,1};   //W[i]表示第i件物品的价值  
int dp[N+1][V+1];

int CompletePack(){  
	for(int i=1;i<=N;i++){
		for(int k=0;k*C[i]<=V;k++)
			dp[i][V]=max(dp[i][V],dp[i-1][V-k*C[i]]+k*W[i]);
	}
	return dp[N][V];
}
int main()  
{  
	memset(dp,0,sizeof(dp));
    cout<<CompletePack();  
    return 0;  
} 

(三)优化数据结构
注意到状态方程中i只与i-1有关,可以消去一维,但是如果直接由上面的代码消维是不成的,因为我们用到了上一状态的V-k*C[i],也就是我们在进入下一状态前,这一状态的每一个dp[v],0<=v<=V,必须都是已知的,但是以上代码是对k进行遍历,和v无关,所以要对v进行遍历,那该如何写呢?
从状态方程中找线索,dp[i][v]=max{dp[i-1][v-k*C[i]]+k*W[i]},0<=k*C[i]<=V
我们再把它展开一下看,dp[i][v]=max{dp[i-1][v],dp[i-1][v-C[i]]+W[i],dp[i-1][v-2C[i]]+2W[i]……}
再来看我们需要的v,这个v范围是0<=v<=V,将v的范围带入上式看,我们发现虽然v-C[i],v-2C[i]看起来很繁琐,但是追根究底范围也一定是在0到V之间,
再来看它们之间的关系,发现后一个元素永远比前一个元素多了一个W[i],如果我们事先将dp[v-C[i]]+W[i]的值得到,那么当v增加一个C[i]时,取dp[v-C[i]],这里dp[v-C[i]]就是dp[v-C[i]]+W[i],然后再加上一个W[i],即dp[v-C[i]]+W[i]不就是取得两件i物品所获得的价值了吗!想到这,代码就可以实现了:
#include <iostream>  
#include <algorithm>  
using namespace std;  
  
#define N 3//物品个数  
#define V 10//背包容量  
  
int C[N+1]={0,10,4,5};  //Ci表示第i件物品的费用   (i从1开始)  
int W[N+1]={0,2,2,1};   //W[i]表示第i件物品的价值  
int dp[V+1];

int CompletePack(){  
	for(int i=1;i<=N;i++){
		for(int v=C[i];v<=V;v++)
			dp[v]=max(dp[v],dp[v-C[i]]+W[i]);
	}
	return dp[V];
}
int main()  
{  
	memset(dp,0,sizeof(dp));
    cout<<CompletePack();  
    return 0;  
} 

发现了吗?代码和01背包问题就内层循环的顺序不同而已,但是实际意义可是大不相同啊

(四)深入优化
如果再继续优化也是可以的,如果有两件物品i和j,有C[i]>C[j],W[i]<W[j],也就是i物品所占容量大于j物品,但是i物品的价值却低于j物品,那么i物品就可以不用考虑了

王川
2014/02/19



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值