算法练习题26---动态规划“01背包问题”


前言

通过一道例题来阐述一下我对于0/1背包问题的想法,0/1背包问题是动态规划思想中一个非常经典的问题。应用非常广泛,主要解决了在复杂条件下的最大价值问题,下面来结合一道例题介绍一下这种思想。

一、背包问题概述

有多个物品,重量不同、价值不同,以及一个容量有限的背包,选择一些物品到背包中,这些物品不能被分割,只能完整地装入背包中,问怎样装才能使装进背包的物品总价值最大。

比如现在有4个物品,重量依次是2,3,6,5,价值分别是6,3,5,4,背包容量为9

0/1背包问题中最重要的数据结构就是一个二维表dp[num][value],其中num表示提供前num种物品选择,而value则表示在该选择下,对应的背包大小,综合起来看,dp[num][value]就表示在前num种物品选择条件下,在背包容量为value的情况下能够产生的最大价值。

那么这个数组是如何进行赋值的呢?

这里我总结为两个for循环加一个状态转移方程。程序的核心结构如下:

for(int i=1;i<=num;i++)  //物品种类
{
    for(int j=weight[i];j<=v;j++)  //背包容量
    {
        //装入当前这个物品,或者不装,选取能够产生价值最大的一种情况
        dp[i][j]=max(dp[i-1][j-weight[i]]+value[i],dp[i-1][j]);  //状态转移方程
    }
}

外层循环控制物品的可用种类,也就是前述的num,表示第1~i种物品都是可用的,但不一定都要装入,内层循环控制背包的容量,背包的容量从第 i 种物品的重量开始,因为背包容量小于第 i 中物品的重量时,肯定是无法装入第 i 种物品的,因此没有意义要跳过。那么状态转移方程则会进行比较,比较在装入第 i 种物品后,还是不装入第 i 种物品哪一个会使背包的价值更大,这就是动态控制背包价值的关键。

比如 dp[1][2]=max(dp[0][2-weight[1]]+value[1],dp[0][2]),其中weight[1]=2,value[1]=6,显然dp[1][2]=6

那么 dp[2][3]=max(dp[1][3-weight[2]]+value[2],dp[1][3]),其中weight[2]=3,value[2]=3,而dp[1][3]=6,dp[1][0]=0,那么综合比较后,因为6>3,最终会选择不装物品2,装物品1,即dp[2][3]=6,这就是动态规划的核心,循环结束后一定能够保证背包的每一个状态都是最大价值。

二、一道例题

取自hdu 2602 “骨头收集者”

“骨头收集者”带着体积为V的背包去捡骨头,已知每个骨头的体积和价值,求能装进背包的最大价值。N≤1000,V≤1000。

输入:第1行是测试数量,第2行是骨头数量和背包体积,第3行是每个骨头的价值,第4行是每个骨头的体积。

1
5 10
1 2 3 4 5
5 4 3 2 1

输出:最大价值

14

三、具体代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		int num,v;
		cin>>num>>v;
		int bone_value[100];   //骨头对应的价值
		int bone_weight[100];  //骨头对应的重量
		int dp[100][100];  //dp数组,行坐标代表当前骨头的种类,列坐标代表当前背包的容量,数组存放最大的价值
		for(int i=1;i<=num;i++)
		{
			cin>>bone_value[i];
		}
		for(int i=1;i<=num;i++)
		{
			cin>>bone_weight[i];
		}
		for(int i=1;i<=num;i++)
		{
			for(int j=bone_weight[i];j<=v;j++)
			{
				//装入当前这个骨头,或者不装,选取能够产生价值最大的一种情况
				dp[i][j]=max(dp[i-1][j-bone_weight[i]]+bone_value[i],dp[i-1][j]);  //状态转移方程
			}
		}
		cout<<dp[num][v]<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨大熊的代码世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值