动态规划算法实例三则

来源:互联网 发布:必修3基本算法语句ppt 编辑:程序博客网 时间:2024/06/05 06:53

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

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

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

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


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

用动态规划来解,首先得考虑状态和状态转移方程。如果我们把题述数组看成序列,那么是不是可以用序列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)

//动态规划方法dppublic 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

0 0
原创粉丝点击