三类背包问题

01背包:有n件物品(每件都只有一个)和一个体积为V的背包。第i件物品的价值为val[i],体积为m[i]。现在需要在不超过背包容量的情况下,使得背包里的东西价值最大?

之所以叫这类问题为01背包,是因为每件物品都只有一个,那么就只有两种选择,放or不放。

可以定义二位数字dp[i][j] 表示在一个容量为j 的背包中放前i种物品得到的最大价值。(问题的状态)

由此可推得状态转移方程 dp[i][j]=max(dp[i-1][j],dp[i-1][j-m[i]]+val[i])。(状态转移方程)

---------------------------------------------------------------------------------------------------------理解---------------------------------------------------------------------------------------------------------------------

考虑第i个物品取还是不取。

若第i个物品不取,则dp[i][j]=dp[i-1][j]表示价值没有变。

若第i个物品要取,则dp[i][j]=dp[i-1][j-m[i]]+val[i]  表示在容量为j的背包,m[i]的部分放物品i,剩下j-m[i]的空间放i-1个物品。(若i的大小超过j,也是不取。)

然后比较2种情况的价值大小,取大的。

---------------------------------------------------------------------------------------------------------优化---------------------------------------------------------------------------------------------------------------------

考虑在更新第i行时只会利用第i-1行,而i-1行前面的都已经没有用了并且在后面的更新中也不会用到。所以可以用一维数组优化。

dp[j] 的意义和之前的一样,但循环时仍需i从1->n循环,而j从V->m[i]循环。原因是更新时需要前面的数据,所以需要从后面更新,而且当背包容量小于第i个物品的大小时,就可以不用优化了。

一道简单的01背包问题hdu 2602

核心代码

//N为物品数,V为背包总容量,第i个物品的大小为w[i],价值为v[i]
//二维数组
for(int i=1;i<=N;i++)
    for(int j=0;j<=V;j++)
        if(j>=w[i]) dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
        else dp[i][j]=dp[i-1][j];
//一维数组优化
for(int i=1;i<=N;i++)
    for(int j=V;j>=w[i];j--)
        dp[j]=max(dp[j-w[i]]+v[i],dp[j]);

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


完全背包: 同样有n件物品(但每件有无限多个)和一个体积为V的背包。第i件物品的价值为val[i],体积为m[i]。现在需要在不超过背包容量的情况下,使得背包里的东西价值最大?

状态和01背包的状态相同,也用dp[i][j] 表示在一个容量为j 的背包中放前i种物品(每种物品可放多个)得到的最大价值。(问题状态)

dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*m[i]]+k*val[i])   0<k*m[i]<=j  (状态转移方程)

---------------------------------------------------------------------------------------------------------理解---------------------------------------------------------------------------------------------------------------------

与01背包相似,不同之处在于对于每一种物品枚举放多个。

在两层循环内继续套一层循环为 k 0->(k*m[i]<=j)为止

---------------------------------------------------------------------------------------------------------优化---------------------------------------------------------------------------------------------------------------------

同样也是一维数组做优化,还有其他优化比如将一种物品拆成多种物品,利用二进制表示。下面为一位数组的优化,因为它的复杂度最低。

在选第i种物品时,背包可能已经选入了第i种物品。所以需要的是dp[i][j-m[i]]而不是dp[i-1][j-m[i]]。

代码如下:

//n为物品数,V为背包总容量,第i个物品的大小为m[i],价值为val[i]
    //二维数组
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++)
            for(int k=1;k*m[i]<=j;k++)
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*m[i]]+k*val[i]);
    //一维优化
    for(int i=1;i<=n;i++)
        for(int j=m[i];j<=V;j++)
            dp[j]=max(dp[j],dp[j-m[i]]+val[i]);


完全背包 HDU 1114

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


多重背包:同样有n件物品和一个体积为V的背包。第i件物品的价值为val[i],体积为m[i],并且有
n[i]个。现在需要在不超过背包容量的情况下,使得背包里的东西价值最大?

跟完全背包的思路基本一样,状态仍是dp[i][j] 表示在一个容量为j 的背包中放前i种物品(每种物品可放多个)得到的最大价值。(问题状态)

dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*m[i]]+k*val[i])   0<k<=n[i] || k*m[i]<=j  (状态转移方程)

---------------------------------------------------------------------------------------------------------理解---------------------------------------------------------------------------------------------------------------------

对于每种物品,选取的个数有了限制,其余的没多大变化,也就是说最内层的循环结束的限制条件发生了改变。

---------------------------------------------------------------------------------------------------------优化---------------------------------------------------------------------------------------------------------------------

也可以将一种物品拆成多种物品,比如说一种物品有13个,因为知道每个数字都可以有二进制表示,那么这种物品就可以拆为(1*m[i],1*val[i]),(2*m[i],2*val[i]),(4*m[i],4*val[i]),(6*m[i],6*val[i])这四种物品。然后即可转化为01背包求解。

伪代码如下:

枚举每一个物品,对每一个物品i调用函数multiplepack(m[i],val[i],n[i])

void completepack(int weight,int value){
	for(int i=weight;i<=V;i++)
		dp[i]=max(dp[i],dp[i-weight]+value);
}
void zeroonepack(int weight,int value){
	for(int i=V;i>=weight;i--)
		dp[i]=max(dp[i],dp[i-weight]+value);
}
void multiplepack(int weight,int value,int num){
	if(weight*num>=V){
		completepack(weight,value);
		return ;
	}
	int k=1;
	while(k<num){
		zeroonepack(k*weight,k*value);
		num-=k;
		k*=2;
	}
	zeroonepack(num*weight,num*value);
}

另外一种优化利用到了优先队列。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值