数据结构与算法--动态规划篇

动态规划(dynamic programming)

概念

可以将一个复杂的大问题分解为简单的小问题,通过求解小问题来得出大问题的解。

性质

无后效性:某一阶段状态确定之后,这阶段之后的发展不受这阶段以前各阶段的状态影响。即未来与过去无关

最优子结构:大问题的最优解可以由小问题的最优解推出

重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。

适用范围

具有重叠子问题和最优子结构性质的问题

分治与动态规划的区别

共同点:二者都要求原问题具有最优子结构性质,都是将原问题分而治之,分解成若干个规模较小(小到很容易解决的程序)的子问题.然后将子问题的解合并,形成原问题的解.

不同点分治法将分解后的子问题看成相互独立的,通过用递归来做。

    动态规划将分解后的子问题理解为相互间有联系,有重叠部分,需要记忆,通常用迭代来做。

步骤

  • 问题抽象
  • 建模(最终要求什么)
  • 约束条件(满足什么样的条件)
  • 确定具有最优子结构
  • 定义状态
  • 大问题和小问题状态转移方程
  • 填表
  • 根据表的结果寻求解的组成

问题1:0-1背包问题

有n 个物品,它们有各自的大小和价值,给定容量cap的背包,如何让背包里装入的物品具有最大的价值总和?

分析步骤

问题抽象:对于每个物品,都有可能要或者不要,因此采用(X1,X2,...Xi,...,Xn)代表第i个物品要或者不要。每个物品的价值为Vi,每个物品的大小为Wi。

建模:最大化价值。max(sum(Xi * Vi))。

约束条件:sum(Xi * Wi) < cap

最优子结构:对每一阶段均可表示成前i个物品组合是否可以使当前阶段总价值最大,这一问题在不同的阶段均适用。

定义状态:V(i,j)--前i个物品最佳组合的总价值,i--表示前i个物品,j--表示剩余容量

状态转移方程:对第i个物品,有两个选择--要或者不要。要--不一定当前总价值最大;不要--最大价值等于前一阶段总价值。

判断条件为--均不能超过剩余背包容量。

V(i,j) = V(i -1,j) , Wi > j; -- 不要第i个物品

V(i,j) = max{V(i -1,j - Wi) + Vi , V(i -1,j)} , Wi <= j; -- 要第i个物品

static int[][] findMax(int cap, int n, int[] value, int[] weight, int[] item){
		int[][] v = new int[n + 1][cap + 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;
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= cap; j++) {
				if(weight[i - 1] > j) {  //大小和价值数组均要减1才能不越界
					v[i][j] = v[i - 1][j];
				}else {
					if(v[i - 1][j] < (v[i - 1][j - weight[i - 1]] + value[i - 1])) {
						v[i][j] = v[i - 1][j - weight[i - 1]] + value[i - 1];	
					}	
					else { 
						v[i][j] = v[i - 1][j];	
					}
				}
			}
		}
		return v;
}
public static void main(String[] args) {
		int n = 4;
		int cap = 8;
		int[] value = new int[n];
		int[] weight = new int[n];
		System.out.println("请输入每个物品的价值:");
		for(int i = 0; i < n; i++) {
			Scanner in = new Scanner(System.in);
			if(in.hasNextInt())
				value[i] = in.nextInt();
		}
		System.out.println("请输入每个物品的大小:");
		for(int i = 0; i < n; i++) {
			Scanner in = new Scanner(System.in);
			weight[i] = in.nextInt();
		}
		int[] item = new int[n];
		int[][] res = findMax(cap, n, value, weight,item);
		System.out.println(res[n][cap]);
}

参考:

https://www.cnblogs.com/raichen/p/5772056.html

https://www.zhihu.com/question/23995189/answer/613096905

https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html

猜你喜欢

转载自blog.csdn.net/qq_40722284/article/details/92016493