对0-1背包问题的理解

首先声明的是这是一篇非常非常基础的对0-1背包问题的理解。

这两天看了动态规划初步,看到了0-1背包问题。看到这里的时候感觉理解有点困难。

问题简介:有一个体积为C的背包,要往里面装n中物品,第i种物品的体积为Vi,质量为Wi,求出背包里面最多能装下多重的物品。

刘汝佳对这个问题的分析是引用了前面的回朔法来理解,但是我觉得这种理解的方法还是有些抽象。

他给出了一个状态转移方程,d(i,j)=max{ d(i+1,j),d(i+1,j-V[i])+W[i] }。d(i,j)的意义是在第i层剩余体积为j 时的最大重量。因为是回朔法,所以答案就应当是d(i,j)。

代码如下:(d[n+1][ ]这些元素所有的值都是0)

for(int i = n ; i >= 1; i--)
	for(int j = 0; j <= C; ++j)
	{
		d[i][j] = (i==n) ? 0 : d[i+1][j];
		if(j>=V[i]) d[i][j] = d[i][j] > (d[i+1][j-V[i]]+W[i]) ? d[i][j] : d[i+1][j-V[i]]+W[i] ;
	}

刚开始的时候对这个代码以及他的状态转移方程都不是很理解。参考一下前面的硬币问题。

硬币问题简介:有n中面值的硬币,现在要用他们来表示S元钱,求使用的硬币最多的数量以及最少的数量。

这里,很容易列出状态转移方程(以最少为例),money[i]=min{ money[i],money[i-V[i]+1  }。理解起来也很简单,money[i]表示的是当钱币总和为i 时,使用的最少的钱币数量。V[i]的含义就是第[i]种钱币的面值。当钱币的总和为i 时,使用的钱币数量最小值就是当前已经得到的最小值与从总面值为i-Vi到i的钱币数量的最小值中更小的那个。

代码如下:

for(int i=1;i<=S;i++)
	min[i]=INF;
for(int i=1;i<=S;++i)//S是要达到的总和 
	for(int j=1;j<=n;++j)//n是钱币面值的种类
	if(i>V[i])min[i] = min[i] < (min[i-V[j]]+1) ? min[i] : min[i-V[j]]+1 ; 

这样,利用状态转移方程就列出了一个递推关系式。我们并不关心是如何达到 i-Vi 这个总和的,我们只需要知道从 i-Vi 到 i 需要增加一个硬币即可。

回过头来看现在面临的0-1背包问题。为什么这个问题会使用有两个参数的状态转移方程呢?在硬币问题中,每种硬币的数量是可以无限增加的,但是在背包问题中,每个物品的数量却只有一个。这就造成了两个问题的细微差别。

现在用这种方式来理解背包问题,d(i,j)表示从 i+1 个物品中选择一个出来使体积达到 j 的时候重量的最大值(有点绕)。

从代码中看出,取物品的顺序给限定了(for(int i=n;i>=1;--i)),表示是从最后一个物品开始取,一个一个地放进背包,这样就使得取的物品不会重复。数组的第二维就表示目前背包中已经有的体积(跟硬币问题一样,我们并不关心是怎么样到达这个体积的,我们只关心从体积 i-V[i] 到i 会增加的重量是 W[i])。

在后面又介绍了另一种用于背包问题的方法,是这种方法“对称”的方法。感觉用我的那种理解方法的话,这种方法更得当

for(int i = 1;i<=n;++i)
	for(int j=0;j<=C;++j)
	{
		f[i][j] = (i==1 ? 0 : f[i-1][j]);
		if(j>=V[i]) f[i][j] = f[i][j] > (f[i-1][j-V[i]]+W[i]) ? f[i][j] : f[i-1][j-V[i]]+W[i] ;
	}

发现了吗?这里跟上面的代码不同之处就是外层循环的顺序变了。在这里,放物品的顺序就是从第1个开始往里面放,一直放到最后一个。所以,最终的答案也就是f(n,C)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值