数据结构与算法(十九)——动态规划

动态规划算法

  1. 动态规划(Dynamic Programing)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法
  2. 动态规划算法与分治算法类似,其基本思想也是将待求解的问题分成若干个子问题,然后从这些子问题的解得到源问题的解
  3. 与分治法不同的是,适合于动态规划算法求解的问题,经过分解得到的子问题往往不是互相独立的(即下一个阶段的求解是建立在上一个阶段上的)
  4. 动态规划可以通过填表的方式来逐步推进,从而得到最优解

背包问题

有一个背包,容量为4磅,现有如下物品

物品 重量 价格
吉他 1 1500
音响 4 3000
电脑 3 2000

1. 要求达到的目标为装入背包的总价值最大,并且重量不超出
2. 要求装入的物品不能重复

思路分析

  • 背包问题主要是指一个给定容量的背包,若干具有一定价值和重量的物品,如何选择物品放入背包使物品的价值最大,其中又分为01背包和完全背包(这里的问题是指01背包,完全背包是指每一种物品都有无限件可以使用)
  • 利用动态规划,每次遍历到第i个物品,根据w[i]和v[i]来确定是否需要将物品放入背包中。即对于给定的n个物品,设v[i]、w[i]分别为第i个物品的价值和重量,C为背包的容量。再令v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大值(即依次增加背包容量,然后根据前一个背包的容量的最优解的累计得到后一个容量的最优解)
  • 遍历物品的目的是细分物品的放入,依次增加一个物品供放入,来找到每次的最好的放入方式;从0增加背包容量是为了在某次当前物品放入时,对剩余容量的方法进行记录

步骤

  1. 首先遍历到吉他,重1磅,价格1500,从0开始依次增加背包的容量,如果能够将吉他放入,则将吉他放入,此时还只有吉他,所以不用考虑其他因素
  2. 再遍历到音响,重4磅,加个3000,从0开始依次增加背包容量,如果不能将音响放入,则表示还是只能按照之前的放法,则复制上一次遍历对应背包容量的值,如能够将音响放入,那么将音响放入,然后看有没有剩余空间,如果有,那么找到上一轮次的剩余空间(因为物品不能重复放入)对应的背包容量放入的价格,两个价格相加,然后再跟上一轮次相同容量的价格作比较,取大值

公式

i:物品的编号,第i个物品
j:背包的容量
w[i]:第i个物品的重量
v[i]:第i个物品的价格
v[i][j]:前i个物品装入容量为j的背包的最大价值

  1. v[i][0]=v[0][j]=0——当前没有物品可放,或者背包没有容量的时候,价值都是0
  2. 当w[i]>j时,v[i][j]=v[i-1][j]——当前物品的重量大于背包的容量,取上一个物品相同容量的最大价值
  3. 当j>=w[i]时,v[i][j]=max{v[i-1][j],v[i-1][j-w[i]]+v[i]}——当前物品的重量小于或等于背包的容量,在上一个物品相同容量的最大价值,和当前物品的价值加上剩余容量对应上一轮次的价值之和,两者最大值

代码实现

/***
 * 背包问题
 * @author laowa
 *
 */
public class Knapsack {

	public static void main(String[] args) {
		// 物品的重量
		int[] w = {1,4,3};
		// 物品的价格,val[i]就表示v[i]
		int[] val = {1500,3000,2000};
		// 背包的重量
		int m = 4;
		// 物品的个数
		int n = val.length;
		
		// 初始化二维数组,v[i][j]表示前i个商品在容量为j的背包中存入的最大价值
		int v[][] = new int[n+1][m+1];
		
		// 初始化第一行和第一列
		for(int i=0;i<v.length;i++) {
			v[i][0] = 0;
		}
		for(int i=0;i<v[0].length;i++) {
			v[0][i] = 0;
		}
		
		// 外层循环遍历物品,内层循环从1开始递增背包容量
		// 因为已经初始化过,这里就不需要处理第一个公式
		for(int i=1;i<v.length;i++) {
			for(int j=1;j<v[0].length;j++) {
				// 当前商品的重量大于背包的容量,因为这里遍历的是二维数组v,所以取w和val的时候需要i-1
				if(w[i-1]>j) {
					//沿用上一次的价格
					v[i][j] = v[i-1][j];
				}else {
					// 当前商品的重量小于等于背包的容量
					v[i][j] = Math.max(v[i-1][j], val[i-1]+v[i-1][j-w[i-1]]);
				}
			}
		}
		print(v);
	}
	
	
	/**
	 * 打印数组
	 * @param array 需要打印的数组
	 */
	private static void print(int[][] array) {
		for(int i=0;i<array.length;i++){
			System.out.println(Arrays.toString(array[i]));
		}
	}

}

你可能感兴趣的:(数据结构,动态规划,算法,数据结构)