DP背包小结(2)(结构完整,内容简洁,状态方程篇)


0 背包小结2,以下状态转移代码,假设所有相关变量及数组都已初始化,物品对应体积和价值都从下标为1的数组开始存储,dp[]或者dp[][]是各个状态下的背包内所装物品总价值。

对于下面用到完全背包模型的,都可以预先进行一步优化,筛选掉所有满足volume[i]<volume[j]&&value[i]>value[j]的i和j物品,对于随机生成的数据来说,可以删掉很多不符合的物品。


1 01背包(背包总容纳bag,有type种物品,每种物品只有一个选择放还是不放,给出对应体积vlume[i]、对应价值value[i])

1)二维数组(建议充分理解,而不用之做题)(因为容易忘记将因体积原因放不进去的状态赋值,以及最后遍历v:0->bag寻找最大值)

//01背包 二维数组
for(int i=1;i<=type;i++){
    for(int j=0;j<=bag;j++){
		if(j>=volume[i]){
			dp[i][j]=max(dp[i-1][j],dp[i-1][j-volume[i]]+value[i]);
		}
		else{
			dp[i][j]=dp[i-1][j];//注意
		}
	} 
}
    int maxx=0;
    for(int j=1;j<=bag;j++){
        maxx=max(maxx,dp[type][j]);
    }

//dp[i][j]指选择完第i个物品放或不放后 背包容量为j时,背包内所有物品的价值
//二维数组因为不能像一维数组一样自动更新,所以如果第i个物品装不进去时,要使dp[i][j]继承上一个物品放置之后的状态dp[i-1][j]而不是维持初始值。

2)一维数组

//01背包 一维数组
for(int i=1;i<=type;i++){
	for(int j=bag;j>=volume[i];j--){
		dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
	}
}
//01背包,每种物品只有一个,该种物品的值不叠加,所以倒序,详见上一篇博客 DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP、二维DP)

HDU2602 裸01背包




2 完全背包(背包总容纳bag,有type种物品,每种物品有无限多,给出对应体积vlume[i]、对应价值value[i])

//完全背包 一维数组
for(int i=1;i<=type;i++){	
    for(int j=volume[i];j<=bag;j++){
		dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
	}
} 
//完全背包,每种物品无限多个,该种物品的值可以叠加,所以直接正序,详见上一篇博客 DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP、二维DP)

HDU4508 裸完全背包



3 多重背包 

①耗用更多内存,来换取更快的时间:

(背包总容纳cash,有n种物品,每种物品有给定数量species[i].num,每种物品有给定的价值species[i].deno,这里物品的体积等同于价值。)

(使用的number[j]是指,第i个物品在当前的背包内存储金额j的情况下使用的数量,正是这个数组的使用来做到用空间换时间)

        for(int i=0;i<n;i++){
            memset(number,0,sizeof(number));
            for(int j=species[i].deno;j<=cash;j++){
                if(dp[j]<dp[j-species[i].deno]+species[i].deno&&number[j-species[i].deno]+1<=species[i].num){
                    dp[j]=dp[j-species[i].deno]+species[i].deno;
                    number[j]=number[j-species[i].deno]+1;
                }
            }
        }


②耗用更多时间,来换取更少内存使用:

(背包总容纳bag,有type种物品,每种物品有给定的数量,给出对应体积vlume[i]、对应价值value[i]、对应数量number[i])

注意!倒序! 

以下两者都是利用倒序  每一种物品都只放一个的特性,前者是该类物品放k次每次都放一个,后者是该类物品放一次 一次从1到k 放入个数依次增大

//多重背包,一维数组
for(int i=1;i<=type;i++){
	int minn=min(number[i],bag/volume[i]);//这里
	for(int k=1;k<=minn;k++){
		for(int j=bag;j>=volume[i];j--){//注意是倒序,否则就错了
			dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
		}
	}
}

POJ1276找零钱



//防止给出的该类物品个数大于实际上能存储的该类物品最大数量(有些用母函数的代码,就是在这里判断一步,如果给出的数量大于背包总容量除以该类物品体积得到的数量,那么就认为该类物品是取不完的 可以转化为完全背包来做,否则就转化为01背包)

OR

for(int i=1;i<=type;i++){
	for(int j=bag;j>=volume[i];j--){//注意是倒序,否则就错了
		for(int k=1;k<=number[i]&&(volume[i]*k<=j);k++){//k=0开始就是不放等同于dp[j],所以不用从0开始
			dp[j]=max(dp[j],dp[j-volume[i]*k]+value[i]*k);
		}
	}
    }
HDU2191 裸多重背包



4二维费用的背包问题 (隐晦表示二维费用背包:有时候题目交代背包最多存x件物品,这是隐晦地表示背包能存件数也是一种费用。)(加多重背包的,类似,加数量那一步循环)

//二维费用背包(两种代价) 完全背包(每种物品无限多个)——所以正序
for(int i=1;i<=type;i++){
    for(int j=volume[i];i<=bag_volume;i++){
        for(int l=weight[i];l<=bag_weight;l++){
            dp[j][l]=max(dp[j][l],dp[j-volume[i]][l-weight[i]]+value[i]);
        }
    }
}

OR

//二维费用背包(两种代价) 01背包(每种物品只有一个)——所以倒序
for(int i=1;i<=type;i++){		
    for(int j=bag_volume;j>=volume[i];j--){
        for(int l=bag_weight;l>=weight[i];l--){
            dp[j][l]=max(dp[j][l],dp[j-volume[i]][l-weight[i]]+value[i]);
        }
    }
}

HDU2159 裸二维背包


5混合背包

看情况组合,部分多重背包在数量足够的情况下可以转化为完全背包,而完全背包又可以转为01背包处理

HDU3591混合背包-经典



6分组背包


7有依赖背包

8泛化物品

9部分算法优化以及背包问题问法变化

1)二进制优化

//注意,number[i]已被改动,如果不想原来的数组里改动,可以另存一个数组里、参考下面给出例题的解题报告。
for(int i=1;i<=n;i++){
    for(int k=1;k<=number[i];k*=2){
        for(int j=bag;j>=volume[i]*k;j--){
            dp[j]=max(dp[j],dp[j-volume[i]*k]+k);
        }
        number[i]-=k;//!!!
    }
    for(int j=bag;j>=volume[i]*number[i];j--){
        dp[j]=max(dp[j],dp[j-volume[i]*number[i]+number[i]);
    }
}
HDU3591混合背包-经典


剩下的再写、


对应题目,请参考本博客DP一类下有关背包的题目。

(本文内容结构参考了dd大牛《背包九讲》,链接之一,http://www.cnblogs.com/jbelial/articles/2116074.html)(注意里面有小错误)




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值