动态规划练习3

       今天解决动态规划里面最著名的问题之一:背包问题。

       背包问题的描述是:已知:有一个容量为V的背包和N件物品,第i件物品的重量是w[i],收益是c[i]。

       问题:在不超过背包容量的情况下,最多能获得多少价值或收益。

       首先应该找到状态表示方程。首先看题目中描述的两个状态:容量,收益,以及最后要求的,在背包容量为V的情况下,i个物品放入背包里面的最大收益!  很容易想到,用一个数组f[i][j]表示,在容量为j的情况下,把i个物品放入背包里面的最大收益。

        找到状态方程了(其实就是问题的数组表示,一般就是一维数组或者二维数组),然后就应该找到状态方程转换公示。个人觉得其实找状态方程比较重要,找准状态方程,转换公示就比较容易了。

        首先,前i-1个物品放入到所有容量的背包的最大收益都已经算出来了(动态规划里面的假设),那么怎么求将i-1个物品放入到容量为j的背包?  

        首先应该确认一点:求i个物品放入容量为j的背包的最大收益,只和i-1有关,和i-2是无关的,因为i-2在求f[i-1]的时候已经确认了。

        把物品放入背包有两种情况:放,或者不放。如果不放或者放不下,那么f[i][j]应该等于f[i-1][j],如果放,那么f[i][j] = max{f[i-1][j-w(i)] + c[i],f[i-1][j]};

        解释一下为啥:首先,将第i个物品放入背包,那么背包里必须有足够的剩余空间。不必管背包里是否需要去掉一个物品,f[i - 1][j - w(i)]是什么意思?这是指前i-1个物品放入到容量为j-w(i)的背包的时候获得的最大收益,令他加上c(i),即是让第i个物品放入背包里的最大收益。

        f[i-1][j]就是第i个物品不放入背包的最大收益。两者取大的,就是前i个物品放到容量为j的背包的最大收益。

       其实这很像贪心法。。。   不过他是二维贪心。如果物品是3个,背包容量是3,那么他的处理流程是这样的:

第1步  计算前1个物品放入容量为1的背包的最大收益
第2步 计算前1个物品放入容量为2的背包的最大收益
第3步 计算前1个物品放入容量为3的背包的最大收益
第4步 计算前2个物品放入容量为1的背包的最大收益
第5步 计算前2个物品放入容量为2的背包的最大收益
第6步 计算前2个物品放入容量为3的背包的最大收益
第7步 计算前3个物品放入容量为1的背包的最大收益
第8步 计算前3个物品放入容量为2的背包的最大收益
第9步 计算前3个物品放入容量为3的背包的最大收益

每一步都是最优解,每一步都尽可能利用前一步的结果,最终结果也是最优解。

   代码:

/**
 * DPTest2 : 动态规划练习2 背包问题联系
 * 
 * 背包问题描述: 已知:有一个容量为V的背包和N件物品,第i件物品的重量是w[i],收益是c[i]。
 * 
 * 限制:每种物品只有一件,可以选择放或者不放
 * 
 * 问题:在不超过背包容量的情况下,最多能获得多少价值或收益
 * 
 * 
 * 
 * @author xuejupo [email protected]
 * 
 *         create in 2016-1-14 下午6:59:53
 */

public class DPTest2 {
	//物品的件数
	private static final int size = 3;
	//包裹的容量
	private static final int packSize = 5;
	private int[] w = new int[size + 1];
	private int[] c = new int[size + 1];
	//定义中间变量
	private int[][] f = new int[size + 1][packSize + 1];
	//初始化
	{
		for(int i = 0; i < size + 1; i++){
			w[i] = new Random().nextInt(3) % 3 + i;
			c[i] = new Random().nextInt(3) % 3 + i;
		}
		System.out.println("背包分别的重量和对应收益为:");
		for(int i = 1; i < size + 1; i++){
			System.out.print("第"+i+"件背包:重量为"+w[i] + ";收益为:"+c[i]);
			System.out.println();
		}
	}
	/**
	 * main:
	 * 
	 * @param args
	 *            void 返回类型
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DPTest2 dp = new DPTest2();
		dp.packTest();
		System.out.println(dp.f[size][packSize]);
	}
	
	/**  
	 * 假设f[i][v] 是前i个物品放入到容量为v的背包时,最大的收益
	 * 比如f[1][1] 是指第一个背包放入到容量为1的背包时的最大收益,
	 * 
	* packTest1:  背包问题练习
	* void  返回类型   
	*/
	private void packTest(){
		for(int i = 1; i <= size; i++){
			for(int j = 1; j <= packSize; j++){
				//背包的容量是j
				if(j >= w[i]){
					f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - w[i]] + c[i]);
				}else{
					f[i][j] = f[i - 1][j];
				}
			}
		}
	}
}

 结果:

背包分别的重量和对应收益为:
第1件背包:重量为1;收益为:2
第2件背包:重量为4;收益为:4
第3件背包:重量为3;收益为:4
6

         

猜你喜欢

转载自709002341.iteye.com/blog/2272098