动态规划之:让你轻松理解背包算法

动态规划之:背包算法最简单的理解


1.常用的算法设计技术:贪心算法,分治和动态规划。

贪心:寻找局部最优,代替全局最优。比如说不带权的区间调度问题,每次选取最早完成时间的作业。找到贪心的标准是最重要的,这种算法设计技术,需要对算法的有效性进行验证,贪心常常不一定有效。

分治:简而言之,分而治之。将一个复杂的大问题分解为若干个子问题求解;

动态规划:将一个复杂问题分解问若干子问题,这些子问题之间有关联和交集,避免重复计算。可以先来看一个简单的更容易理解的例子:走楼梯问题。


欢迎查看相关问题:动态规划之:防止重复计算【经典问题:走楼梯问题,斐波那数列】 【完成】


背包问题是各个领域的经典问题之一,今天就总结下,通俗易懂的帮助大家快速理解该算法。


背包问题:有N件物品和一个容量为W的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

《动态规划之:让你轻松理解背包算法》


定义:OPT(i,w)为在1,2,3,…….,i 这么多件物品中重量和小余w的最优解;

i=0,1,2,3,4,5

wi:  w1,w2,w3,w4,w5————-> 1,2,5,6,7

w: 11  书包容量(重量)不能超过w.


则:

(1). OPT(i,w)=0           if  i<=0

(2). OPT(i,w)=OPT(i-1,w)             if wi>w 【第i个重量超过了总重量W的限制】

(3).OPT(i,w)=max{OPT(i-1,w) ,   Vi+OPT(i-1,w-wi) }    otherwise


上述递归式很重要,如下让我们手工操作一遍,感受下这个过程,请自己画表格,填充试一试,加深理解:

《动态规划之:让你轻松理解背包算法》

上面第一行为0,1,2,………,10,11。意思为总重量限制;

第一列指的是包含物品序号的集合;

在对应的物品序列集合和总重量限制下,白色部分红色的0为填充的值,即当总重量为0限制【第二列列头0】或者物品序号集合为空集合{  }【第二行的行头】时候,最大的价值OPT为红色部分所示的0。

下面根据如下的公式计算下表:

(1). OPT(i,w)=0           if  i<=0

(2). OPT(i,w)=OPT(i-1,w)             if wi>w 【第i个重量超过了总重量W的限制】

(3).OPT(i,w)=max{OPT(i-1,w) ,  Vi+OPT(i-1,w-wi) }      otherwise

继续按照递推公式填满下表:

《动态规划之:让你轻松理解背包算法》

         

          例如计算图标中的红色值是18,过程如下:

OPT(i,w)=OPT(3,5),此时Wi=W3=5>5不成立【Wi>W不成立】,

OPT(i,w)==max{OPT(2,5),18+OPT(2 ,5-5)}==max{OPT(2,5), 18+OPT(2,0)}==max{7,18}=18;

算法描述:

            Input: n,     W1,W2,    ............ ,Wn,  V1,.......Vn
            for w=0  to  W
             M[0,w]=0;

	     for i=1 to n
            for w=1  to  W
            if(wi>w)
			M[i,w]=M[i-1, w];
	   else
			M[i, w]=max{M[i-1, w],vi+M[i-1, w-wi]}
	   return M[n,W];

此算法的时间复杂度:O(nW)   


java代码实现:请理解上面的表格或者递推公式。下面代码复制到java环境改下对应包名即可运行。

package com.mytest.test001;

public class knapspack {
	/**
	 * 有N件物品和一个容量为W的背包。第i件物品的重量是w[i],价值是v[i]。
	 * 求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
	 */
	public static void main(String[] args) {
		int w=11;//背包装入的总重量不能超过该值,使得总价值最大
		int n=5;//五个物品
		int[] value={1,6,18,22,28};//对应物品的价值
		int[] weight={1,2,5,6,7};//对应每个品的重量
		System.out.println("所得结果:"+findMaxValue(w,n,weight,value));
	}

	private static int findMaxValue(int w,int n, int[] weight, int[] value) {
		int[][]max=new int[n+1][w+1];
		
		for(int i=0;i<=w;i++)//M[n,W]
			max[0][i]=0;
		
		for(int j=1;j<=n;j++)
			for(int k=1;k<=w;k++)
				if(weight[j-1]>k){//第j个物品对应重量的下标减1,从0开始。
					max[j][k]=max[j-1][k];//当加入的一个物品重量大于k,这个物品一定不能选
				}else{
					int a=max[j-1][k];//不选第j个物品
					int b=value[j-1]+max[j-1][k-weight[j-1]];//可以选第j个物品,选择这个物品
					max[j][k]=a>b ? a:b;//选择第j个和不选第j个物品,那个大,返回哪个;
				}
		
		//遍历数组结果,打印出来看看
		for (int[] is : max) {
			for (int i : is) {
				System.out.print(i+"  ");
			}
			System.out.println();
		}
		return max[n][w];
		
	}


}


运行效果图:

《动态规划之:让你轻松理解背包算法》

【备注】:显然以上算法的时间复杂度为O(nw)。不是多项式时间内的解法。

【备注】:每个物品有多件,等其他非简单背包问题都可以转化为类似的简单背包问题。

备注】:存在O(n)的近似算法。

    原文作者:动态规划
    原文地址: https://blog.csdn.net/a784586/article/details/63262080
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞