动态规划经典问题----0-1背包问题

    要用动态规划来求解问题,我们首先要理解动态规划的本质是什么?

   动态规划算法与分治法类似,其基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解,与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是独立的。这是什么意思呢,其实就是说:当你求解你分解好的子问题时,有可能用到另一个子问题的解,这个时候,如果之前求完的那个子问题你没有保存结果,那么这次又要重新再求解一次。当问题规模很大时,有可能一个子问题会被求解多次,造成时间上的浪费。所以我们应该保存所有已解决的子问题的答案,不管该子问题以后是否会被用到,只要它被计算过,就将其结果填入表中。

   一般来说,动态规划算法需要搜索所有解的状态,这就需要我们根据具体问题,来决定要用什么数据结构以什么方式来存储解,都存储那些解,子问题怎么划分,子问题划分好了以后,怎么构造最优解。。等一系列问题。。。下面来看动态规划问题的经典实例:0-1背包问题

  问题描述:有n个物品,第i个物品的价值为v[i],重量为w[i],背包的总容量为W。现在需要考虑如何选择装入背包的物品,使装入背包的物品总价值最大,总重量不得超过背包容量。

  用计算机求解这一类问题时,肯定不能用我们人类心算眼看的办法:把这个放上看看行不行,不行再选那个,那个不行再选其他的。这种办法别说计算机,就是人在问题规模大的时候也没法控制。。所以我们必须设计用计算机可以控制的算法

  我们可以用枚举所有可能解的方法来求解这 个问题,计算出这几个物品的所有可能的组合,从中找出值最优的那个。但这个方法当问题规模很大时,需要耗费指数级时间。。

   既然穷举不可以,那么我们就考虑用动态规划法。。

   要看一个问题是不是能用动态规划来求解,首先要看它是否具有最优子结构特征。。最优子结构就是指一个问题的最优解中的包含了子问题的最优解,就是说你要求原问题的最优解,需要通过其子问题的最优解来构造。我们来看一下这个问题是否具有最优子结构

   我们应该怎么分解这个问题呢,总不能一分两半,先求前半部分的,再求后半部分的,然后两加。这样求出来的解不是整体最优。。也根本没法求

   我们可以从整体上考虑,这个问题的最优解中必定包含某几个物品。假设肯定包含物品i,那么最优解中其余的物品肯定是从除去i之后其余的n-1个物品里找。怎么找呢?除去i之后,背包总重量变成了:W-wi,物品个数变成了n-1。而这样又变成了求解当物品个数为n-1,背包容量为W-wi时的最优解,即构成了原问题的一个子问题,你要像求解原问题,这个子问题的解必须先求出,所以这个问题具有最优子结构,可以用动态规划法求解。

    可以这样描述:物品x1,x2...xn-1,xn.背包容量为W。我们求解的过程就是决定哪一个物品放入背包,哪一个物品不放入背包等一系列决策过程。那怎么决定呢?比如xn到底应该不因该放入呢?如果放入的话,问题的最优解V为物品n的价值+(重量为W-wn,n-1个物品时的最优解)(既然包括n,就应该取出物品n的重量)。。如果不放入的话,问题的最优解V就是重量还是为W,n-1个物品的最优解(既然不包括这个物品,那背包总重量不变,但物品数量还是要减去一个)。。所以决定物品n到底放不放入,只要看看,放入的情况下和不放入的情况下那一个求出得解大,如果放入的情况下大,说明选择物品n,否则,不选择。

 

根据以上分析,我们建立以下状态转移方程(什么是状态转移方程,实际上就是动态规划算法的核心,主要决策者就是这个递归方程),设i个物品在背包重量为W时的最优解为V[i,W]。

              则: V[i,W]=max(V[i-1,W-wi]+vi,V[i-1,W]) 

有了这个方程,我们应该如何来设计算法,来求解呢?

 

首先,我们应该设计一个数据结构来保存所有的子问题的解。那所有子问题是那些子问题呢?根据前面的方程我们可以看出,所有的子问题是i(i<=n)个物品在重量为w(w<=W)时的最优解。。

    怎么表示及存储呢?

    我们可以用一个二维数组来存储,Value[n][W].表示n个物品在背包重量为W时的最优值。 所以我们的目标是计算出所有个数的物品在所有可能重量的情况下的最优值。

   到了这里,相信再编码就已经很容易了,只要按照顺序(物品从0--n,重量从0-W)根据状态转移方程依次遍历即可。需要注意的是,当物品为0个或重量为0的时候,所有的值都应该为0. 

 

核心代码如下:

 

	for(int i=0;i<=WEIGHT;++i)
		value[0][i]=0;
	for(i=0;i<6;++i)
		value[i][0]=0;

	for(i=0;i<5;++i)
	{
		for(int weight=1;weight<=WEIGHT;++weight)
		{
			if(w[i]>weight)
			{
				value[i+1][weight]=value[i][weight];
				continue;
			}
			if((value[i][weight-w[i]]+v[i])>=value[i][weight])
				value[i+1][weight]=value[i][weight-w[i]]+v[i];
			else
				value[i+1][weight]=value[i][weight];
		}
	}

求出所有解之后,我们还要构造出最优解来。其实也很简单,看一个物品选择还是没选择,只要查看他和上一个物品的解是否相同即可,如果相同,说明不选择本物品(因为这个方程V[i,W]=max(V[i-1,W-wi]+vi,V[i-1,W]))。。比如:物品n。如果V[n-1,w]==V[n,w],说明不选择此物品,否则,说明选择此物品,并把此时的背包重量减去此物品的重量,然后检测下一个物品以同样的方式。

	for(i=5;i>=1;--i)
	{
		if(value[i][weight]!=value[i-1][weight])
		{
			result[i-1]=1;
	    	weight-=w[i-1];
		}
	}


大体思路就如上所示

 

动态规划的思想确实比较难以理解,尤其是对于我来说,呵呵。因为这种题目没有一种固定的解题套路,需要具体问题具体分析,一种的问题的解题思路很难应用到其他题目上。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值