【Java基础算法】动态规划

Java动态规划算法详解:最长公共子序列与0-1背包问题
本文介绍了动态规划算法的基本思想和设计步骤,通过最长公共子序列和0-1背包问题的具体实例,详细解析了如何应用动态规划求解这些问题。动态规划算法通过保存子问题的解避免重复计算,达到优化时间复杂度的目的。文章提供了详细的解题思路、递归关系以及代码实现,展示了动态规划在最优化问题中的应用。

动态规划

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是相互独立的。若用分治法解这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间。然而,不同子问题的数目常常只要多项式量级。在用分治法求解时,有些子问题被重复计算了许多次。如果能够保存已解决的子问题的答案,而在需要时再找出来已求得的答案,就可以避免大量重复计算,从而得到多项式时间算法。为了达到这个目的,可以用一个表来记录所有已解决的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思想。具体的动态规划算法是多种多样的,但是它们具有相同的填表格式。

动态规划算法适用于解最优化问题。通常可以按以下步骤设计动态规划算法:

  • 1、找出最优解的性质,并刻画其结构特征;
  • 2、递归地定义最优值;
  • 3、以自底向上地方式计算出最优值;
  • 4、根据计算最优值时得到地信息,构造最优解。

步骤1~3是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤4可以省去。若需要求问题的最优解,则必须执行步骤4.此时,在步骤3中计算最优值时,通常需记录更多的信息,以便在步骤4中,根据所记录的信息,快速构造出最优解。

下面以具体的例子说明如何运用动态规划算法的设计思想,并分析可用动态规划算法求解的问题应该具备的一般特征。

最长公共子序列
问题描述

在这里插入图片描述

  • 动态规划算法可有效地解决此问题。下面按照动态规划算法设计地各个步骤设计解决此问题地有效算法。
解题思路

1、最长公共子序列的结构:

穷举搜索法是最容易想到地算法。对X的所有子序列,检查它是否为X与Y的公共子序列。并且在检查过程中记录最长的公共子序列。X的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的每个子序列都相应于下标集{1,2,...,m}的一个子集。因此,共有2^m个不同子序列,从而穷举搜索法需要指数时间。

事实上,最长公共子序列问题具有最优子结构性质。

设序列X = {x1,x2,…,xm}和Y = {y1,y2,…,yn}的最长公共子序列为Z = {z1,z2,…,zk},则:

  • 1、若xm = yn,则zk = xm = yn,且Z(k-1)是X(m-1)和Y(n-1)的最长公共子序列;
  • 2、若xm != yn,且zk != xm,则Z是X(m-1)和Y的最长公共子序列;
  • 3、若xm != yn,且zk != yn,则Z是X和Y(n-1)的最长公共子序列;

其中,X(m-1) = {x1,x2,…,x(m-1)};Y(n-1) = {y1,y2,…,y(n-1)};Z(k-1) = {z1,z2,…,z(k-1)}。

证明方法为:

在这里插入图片描述

由此可见,两个序列的最长公共子序列包含了这两个序列的前缀的最长公共子序列。因此,最长公共子序列问题具有最优子结构性质。

2、子问题的递归结构:

由最长公共子序列问题的最优子结构性质可知,要找出X = {x1,x2,…,xm}和Y = {y1,y2,…,yn}的最长公共子序列,可按以下方式递归计算:当xm = yn时,找出X(m-1)和Y(n-1)的最长公共子序列,然后在其尾部加上xm(yn)即可得X和Y的最长公共子序列。当xm != yn时,必须解两个子问题,即找出X(m-1)和Yn的一个最长公共子序列及X和Y(n-1)的一个公共子序列。这两个公共子序列中较长者即为X和Y的最长公共子序列。

由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算X和Y(n-1)及X(m-1)和Y的最长公共子序列。而这两个子问题都包含了一个公共子问题,即计算X(m-1)和Y(n-1)的最长公共子序列。

在这里插入图片描述

3、计算最优值:

在这里插入图片描述

  • 递归算法:
public static int LCSLength(String X, String Y, int i, int j, int[][] c, int[][] s) {
   
   
    if (i == 0 || j == 0) return 0;
    if (c[i][j] > 0) return c[i][j];
    else if (X.charAt(i) == Y.charAt(j)) {
   
   
        c[i][j] = LCSLength(X, Y, i - 1, j - 1, c, s) + 1;
        s[i][j] = 1;
    } else {
   
   
        int max1 = LCSLength(X, Y, i - 1, j, c, s);
        int max2 = LCSLength(X, Y, i, j - 1, c, s);
        if (max1 > max2) {
   
   
            c[i][j] = max1;
            s[i][j] = 2;
        } else {
   
   
            c[i][j] = max2;
            s[i][j] = 3;
        }
    }
    return c[i][j];
}
  • 非递归算法:
/**
 * LCSLength的非递归算法
 */
public static int NiceLCSLength(String X, String Y, int m, int n, int[][] c, int[][] s) {
   
   
    for (int i = 1; i <= m; ++i) {
   
   
        for (int j = 1; j <= n; ++j) {
   
   
            if (X.charAt(i) == Y.charAt(j)) {
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值