动态规划算法实例三则

动态规划属于不好理解的计算机基本算法之一。

需要经过多次实践,才能体会其精妙之处。

其精妙的地方在于:降低运算量。

下面通过实例理解动态规划解题思路。

实例一:求数组的最大连续和子数组。参考文章

用动态规划来解,首先得考虑状态和状态转移方程。如果我们把题述数组看成序列,那么是不是可以用序列DP来考虑呢?

我们不妨考虑一个这样的序列:1,-3,5,-2,4

a[i]表示这个序列的第 i 个元素,dp[i]表示最后一个元素是a[i]的最大连续和(此乃状态,是不是跟LIS的DP解法有点类似),于是:

dp[0] : a[0] ; ( 1 )

dp[1] : max(dp[0] + a[1] , a[1]) ; ( -2 )

dp[2] : max(dp[1] + a[2] , a[2]) ; ( 5 )

dp[3] : max(dp[2] + a[3] , a[3]) ; ( 3 )

dp[4] : max(dp[3] + a[4] , a[4]) ; ( 7 )

所以:ans = 7 (dp数组的最大值)

于是,我们可以得到状态转移方程:dp[i+1] = max(dp[i]+a[i+1] , a[i+1])

写成代码的话,我们可以忽略掉dp数组,直接用一个变量sum来记录 i 之前的最大增量(因为如果这个增量为负,则变为0)

java源码如下:动态规划求解复杂度为o(n)

	//动态规划方法dp
	public int maxSubArray3(int[] array) {
		int sum = 0;
		int ret = Integer.MIN_VALUE;
		for (int i = 0; i < array.length; i++) {
			sum += array[i];

			if (sum > ret) {
				ret = sum;
			}

			if (sum < 0) {
				sum = 0;
			}
		}

		return ret;
	}

	public static void main(String[] ars) {
		int[] a = { -2, 1, -3, 4, -1, 2, 1, -5, 4};
		MaxSubArray array = new MaxSubArray();
		int max = array.maxSubArray3(a);
		System.out.println(max);
	}

注意:状态转移方程没有在代码中提现出来。

求子数组最大和问题,也可以直接遍历求解。复杂度为o(n^2)

java代码如下:

	//直接遍历求解
	public int maxSubArray4(int[] array) {
		int max = Integer.MIN_VALUE;
		int sum = 0;
		for (int i = 0; i < array.length; i++) {
			sum = 0;
			for (int j = i; j < array.length; j++) {
				sum += array[j];
				if (sum > max) {
					max = sum;
				}
			}
		}

		return max;
	}

实例二:数组分割成两个数组,且两子数组和的差最小。

动态规划解题思路:

1、原数组所有元素之和为sum.

2、子数组和的差最小,则子数组和尽量接近sum/2.

3、问题转化为,求原数组中的子数组,子数组之和尽量接近sum/2,最好等于sum/2.这样差就会为0。

4、转化为01背包问题:sum/2为背包容量。

状态转移方程为(参考文章2):背包容量是SUM/2. 每个物体的体积是数的大小,然后尽可能的装满背包。

dp方程:f[i][V] = max(f[i-1][V-v[i]]+v[i], f[i-1][V] )

f[i][V]表示用前i个物体装容量为V的背包能够装下的最大值,f[i-1][V-v[i]]+v[i]表示第i个物体装进背包的情况,f[i-1][V]表示第i件物品不装进背包的情况。

java源码如下:

package test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SplitArray {
	static int[][] result = new int[20][20];

	static int split(int[] array) {
		int sum = 0;
		int len = array.length;
		for (int i = 1; i < len; i++){
			sum += array[i];
		}
		sum >>= 1;

		//动态规划
		for (int i = 1; i < len; i++) {			//循环数组
			for (int j = 0; j <= sum; j++) {	//背包容量
				if (j >= array[i]) {			//背包容量能放下array[i]
					result[i][j] = Math.max(result[i - 1][j], result[i - 1][j - array[i]] + array[i]);
				} else							//不能放下array[i]
					result[i][j] = result[i - 1][j];
			}
		}

		//查找出被
		int j = sum;
		List<Integer> listA = new ArrayList<Integer>();
		List<Integer> listB = new ArrayList<Integer>();
		for (int i = len - 1; i > 0; i--) {
			if (result[i][j] > result[i - 1][j]) { //找出被选中的元素
				listA.add(new Integer(array[i]));
				j -= array[i];
			} else {
				listB.add(new Integer(array[i]));
			}
		}

		//显示划分结果
		Object[] ret = listA.toArray();
		System.out.println(Arrays.toString(ret));
		ret = listB.toArray();
		System.out.println(Arrays.toString(ret));

		//给出差值
		int sum1 = 0;
		int sum2 = 0;
		for (Integer integer : listA) {
			sum1 += integer;
		}
		for (Integer integer : listB) {
			sum2 += integer;
		}
		int diff = Math.abs(sum1 - sum2);
		return diff;
	}

	public static void main(String args[]) {
		int[] array = { 0, 3, 5, 2, 1, 4 };
		int diff = split(array);
		System.out.println("the diff is: " + diff);
	}
}

运行结果如下:

[2, 5]

[4, 1, 3]

the diff is: 1

实例三:经典背包问题温习(文考文章3)

01背包问题的动态规划转移方程为:

f[i][v] = max{ f[i-1][v] , f[i-1][v - c[i]] + v[i]}

参考文章

1、http://www.mamicode.com/info-detail-511949.html

2、http://www.tuicool.com/articles/ZF73Af

3、http://www.cnblogs.com/bourbon/archive/2011/08/23/2151044.html

时间: 2024-10-07 22:46:33

动态规划算法实例三则的相关文章

动态规划——算法总结(三)

动态规划算法通常用于求解具有某种最优性质的问题.在这类问题中,可能会有许多可行解.每一个解都对应于一个值,我们希望找到具有最优值的解.动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解.与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的.若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次.如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避

揭露动态规划真面目——算法第三章上机实践报告

算法第三章上机实践报告 一.        实践题目 7-2 最大子段和 (40 分) 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值.当所给的整数均为负数时,定义子段和为0. 要求算法的时间复杂度为O(n). 输入格式: 输入有两行: 第一行是n值(1<=n<=10000): 第二行是n个整数. 输出格式: 输出最大子段和. 输入样例: 在这里给出一组输入.例如: 6 -2 11 -4 13 -5

动态规划 算法(DP)

多阶段决策过程(multistep decision process)是指这样一类特殊的活动过程,过程可以按时间顺序分解成若干个相互联系的阶段,在每一个阶段都需要做出决策,全部过程的决策是一个决策序列.动态规划(dynamic programming)算法是解决多阶段决策过程最优化问题的一种常用方法,难度比较大,技巧性也很强.利用动态规划算法,可以优雅而高效地解决很多贪婪算法或分治算法不能解决的问题.动态规划算法的基本思想是:将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子

野生前端的数据结构练习(11)动态规划算法

一.动态规划算法 dynamic programming被认为是一种与递归相反的技术,递归是从顶部开始分解,通过解决掉所有分解出的问题来解决整个问题,而动态规划是从问题底部开始,解决了小问题后合并为整体的解决方案,从而解决掉整个问题. 动态规划在实现上基本遵循如下思路,根据边界条件得到规模较小时的解,小规模问题合并时依据递推关系式进行,也就是说较大规模的问题解可以由较小问题的解合并计算得到.最经典易懂的就是使用递归算法和动态规划算法两种不同的方式来计算斐波那契数列或求阶乘的对比,动态规划算法的特

算法学习三阶段

?? 第一阶段:练经典经常使用算法,以下的每一个算法给我打上十到二十遍,同一时候自己精简代码, 由于太经常使用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都能够把程序打 出来. 1.最短路(Floyd.Dijstra,BellmanFord) 2.最小生成树(先写个prim,kruscal 要用并查集,不好写) 3.大数(高精度)加减乘除 4.二分查找. (代码可在五行以内) 5.叉乘.判线段相交.然后写个凸包. 6.BFS.DFS,同一时候熟练hash 表(要熟,要灵活,代码要

动态规划分析总结——如何设计和实现动态规划算法

进行算法设计的时候,时常有这样的体会:如果已经知道一道题目可以用动态规划求解,那么很容易找到相应的动态规划算法并实现:动态规划算法的难度不在于实现,而在于分析和设计-- 首先你得知道这道题目需要用动态规划来求解.本文,我们主要在分析动态规划在算法分析设计和实现中的应用,讲解动态规划的原理.设计和实现.在很多情况下,可能我们能直观地想到动态规划的算法:但是有些情况下动态规划算法却比较隐蔽,难以发现.本文,主要为你解答这个最大的疑惑:什么类型的问题可以使用动态规划算法?应该如何设计动态规划算法? 动

数据结构-图-经典算法(三)

参考资料 http://www.cnblogs.com/hanchan/archive/2009/09/23/1572509.html http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html http://www.cnblogs.com/RootJie/archive/2012/05/15/2501317.html 四.最短路径 1.从某个源点到其余各个顶点的最短路径 Dijkstra(迪杰斯特拉)算法是典型的单源最短

动态规划算法之滚动数组的求解(C++)

虽然接触动态规划算法已经有一段时间,给一个01背包问题,能够做到一个表格简单粗暴下去,然后求得结果,但心里总觉得对这个算法理解十分不到位,抱着对算法的热爱,网上很多大牛的算法思维实在让我佩服的五体投地.在此讲一讲动态规划中滚动数组的求解方法,算是对这个知识点做一个记录,也希望有写的不妥的地方,大家能不吝赐教. 首先,我们先看看"滚动数组"的例题,大家可以参考http://www.lintcode.com/en/problem/house-robber/ 题意大概就是说:一个盗贼要去偷盗

动态规划算法的理解

什么是动态规划算法? 动态规划算法其实质就是分治思想和解决冗余.因此它与分治法和贪心法类似,都是将待求解问题分解为更小的,相同的子问题,然后对子问题进行求解,最终产生一个整体最优解. 适合采用动态规划法求解的问题,经分解得到的各个子问题往往不是相互独立的.在求解过程中,将已解决的子问题的解进行保存,在需要时可以轻松地找出. 示例如下: Fibonacci数列       0   n=0 f(n)=  1   n=1 f(n-1)+f(n-2)    n>1 如果n=4,则f(4)=f(3)+f(