动态规划的基本思想与例子解析

来源:互联网 发布:不需要备案的国外域名 编辑:程序博客网 时间:2024/05/21 19:21
    经分解得到的子问题有时候往往不是互相独立的。不同子问题的数目常常只有多项式量级。在用分治法求解时,有些子问题被重复计算了许多次。

   Those who cannot remember the pastare doomed to repeat it.        ——George Santayana

   (不取前车之鉴,必将重蹈覆辙。)


1.基本思想

   如果分解得到的子问题不是互相独立的,此时若能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算,从而提高计算效率。

2.解题步骤

I.找出最优解的性质,并刻划其结构特征。(最优子结构)
II.递归地定义最优值。
III.自底向上的方式计算出最优值。
IV.根据计算最优值时得到的信息,构造最优解

3.动态规划的应用例子

(a) 最大连续乘积子数组

I.问题描述:任意取出数组中的若干个连续的数相乘,找出其中乘积最大的子数组
II.解题思路:考虑到负数的存在,即负负得正的情况可能成为乘积最大的子数组,故需要同时并记录找出最大乘积和最小乘积
III.递归结构:maxEnd = max(max(maxEnd * a[i], minEnd * a[i]), a[i]);
                      minEnd = min(min(maxEnd * a[i], minEnd * a[i]), a[i]);
IV.一层for循环,时间复杂度为O(n)
public double maxProductSubstring(double[] a){double maxEnd = a[0];double minEnd = a[0];double maxResult = a[0];for (int i = 1; i < a.length; i++) {double end1 = maxEnd * a[i], end2 = minEnd * a[i];maxEnd = Math.max(Math.max(end1, end2), a[i]);minEnd = Math.min(Math.min(end1, end2), a[i]);maxResult = Math.max(maxEnd, maxResult);}return maxResult;}

(b) 0-1背包问题

I.问题描述:一共有n件物品,对每一件物品i都有两种选择,即装入背包或不装入。一件物品只能装一次且不能装入部分物品
II.递归结构:

public static int package0_1(int[] v, int[] w,int Max){int len = v.length;int[] dp = new int[len];for (int i = 0; i < len; i++) {for (int j = Max; j >= w[i]; j--) {dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}}return dp[len];}

(c) 最长公共子序列(LCS)

I.问题描述:给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列,求X、Y的最长公共子序列
II.最优子结构:

III.递归结构:

IV.对于m*n个子问题均需要计算一次,故时间复杂度为O(m*n)
V.递归结构中空间复杂度为O(m*n),可优化为O(min(m,n))
public static int lcsLength(char[] x, char[] y){int m = x.length;int n = y.length;int k = m > n ? n : m;int[] c = new int[k+1];int leftabove, above;for (int i = 1; i <= m; i++) {leftabove = c[0];  above = c[1];  for (int j = 1; j <= n; j++) {if(x[i-1] == y[j-1]){c[j] = leftabove + 1;}else{c[j] = Math.max(c[j], c[j-1]);}//更新变量leftabove = above;              above = c[(j+1) % n];    }}return c[k];}


0 0