动态规划算法

简述

动态规划(dynamic programming)与分治方法相似,都是通过组合子问题的解来求解原问题。动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治算法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。动态规划方法通常用来求解最优化问题(optimization problem)。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值最小值或最大值)的解。我们称这样的解为问题的一个最优解(an optimal solution),而不是最优解(the optimal solution),因为可能有多个解都达到最优值。
通常按如下4个步骤来设计一个动态规划算法:
1.刻画一个最优解的结构特征。
2.递归地定义最优解的值。
3.计算最优解的值,通常采用自底向上的方法
4.利用计算出的信息构造一个最优解。

钢条切割案例

package s21;

import java.util.Arrays;
/*钢条切割,动态规划
长度为n的钢条怎样切割使售卖价格最大
 */
public class DynamicProgramming {
    //带备忘的自顶向下方法
    public static int memoizedCutRod(int[] p , int n){
        int[] r = new int[n+1]; //记录最优解
        Arrays.fill(r,Integer.MIN_VALUE);
        return memoizedCutRodAux(p,n,r);
    }
    public static int memoizedCutRodAux(int[] p , int n , int[] r){
        int q;
        if(r[n]>0)  //当有记录时直接返回
            return r[n];
        if(n==0)  //长度0,价值0
            q = 0;
        else{
            q = Integer.MIN_VALUE;
            for(int i = 1; i <= n; i++) //每次循环,切割第一段长度遍历
                //递归取剩余长度的最优解
                q = Math.max(q,p[i]+memoizedCutRodAux(p,n-i,r));
        }
        r[n] = q;
        return q;
    }
   //自低向上法
    public static int cutRod(int[] p, int n){
        if(n==0)
            return 0;
        int q = Integer.MIN_VALUE;
        for(int i = 1; i <= n; i++) //每次循环,切割第一段长度遍历
            //递归取剩余长度的最优解
            q = Math.max(q,p[i]+cutRod(p,n-i));
        return q;
    }
    //自低向上法,记录最优解方案
    public static void extendedCutRod(int[] p, int n){
        int[] r = new int[n+1];
        int[] s = new int[n+1];
        int q;
        r[0] = 0;
        for(int j = 1; j <= n; j++){ //循环取每种长度的最大价值
            q = Integer.MIN_VALUE;
            for(int i = 1; i <= j; i++){ //遍历第一段方案
                if(q<p[i]+r[j-i]){  //r[j-i]必处理过
                    q = p[i]+r[j-i];  //找到更优方案,更新价值
                    s[j] = i;  //更新第一段长度
                }
            }
            r[j] = q;
        }
        System.out.println(Arrays.toString(r));//[0, 1, 5, 8, 10]
        System.out.println(Arrays.toString(s));//[0, 1, 2, 3, 2]
        //切割方案
        while(n>0){  //2 2
            System.out.print(s[n] + " ");
            n = n - s[n];
        }
    }

    public static void main(String[] args) {
        //索引长度对应价值
        int[] p = {0,1,5,8,9,10,17,17,20,24,30};
        System.out.println(memoizedCutRod(p,4));  //10
        System.out.println(cutRod(p,4));  //10
        extendedCutRod(p,4);
    }
}

 矩阵相乘最优解

import java.util.Arrays;

public class MatrixMultipleAnalysis {

    public static void main(String[] args) {
        /*矩阵相乘A1*A2*...AN,加()进行最优组合,
        A(N-1) * AN 需要乘法次数m[n-1][n] = p[n-2] * p[n-1] * p[n]
        迭代公式,i-j范围从k处拆解 m[i][j] = m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j] 
         */
        int[] p = {10,100,5,50};
        matrixChainOrder(p);
    }

    public static void matrixChainOrder(int[] p){
        int n = p.length - 1;
        int[][] m = new int[n+1][n+1];
        int[][] s = new int[n][n+1];
        for(int i  = 1; i <= n; i++)
            m[i][i] = 0;
        for(int l = 1; l <= n-1; l++)  //括号内矩阵长度,从2个矩阵相乘扩展到n个
            for(int i = 1; i < n-l+1 ; i++){  //括号内第一个矩阵位置
                    int j = i + l;  //括号内最后一个矩阵位置
                    m[i][j] = Integer.MAX_VALUE;
                    for (int k = i; k <= j - 1; k++) {  //循环拆分位置,取最优解
                        int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
                        if (q < m[i][j]) {
                            m[i][j] = q;
                            s[i][j] = k;
                        }
                    }
            }
        for(int i  = 0; i <= n; i++)
            System.out.println(Arrays.toString(m[i]));
        System.out.println("------------------------------");
        for(int i  = 0; i < n; i++)
            System.out.println(Arrays.toString(s[i]));
    }
}

共同最长子集

public class CommonSubset {

    public static int maxI = 0;
    public static int maxJ = 0;
    public static int max = 0;

    public static void main(String[] args) {
        String s1 = "ACCGGTCGAGTGCGCGGAAGCCGGCCGAA";
        String s2 = "GTCGTTCGGAATGCCGTTGCTCTGTAAA";
        char[] x = s1.toCharArray();
        char[] y = s2.toCharArray();
        longest(x,y);
    }
    public static void longest(char[] x, char[] y){
        boolean[][] arr = new boolean[x.length][y.length];
        int[][] count = new int[x.length][y.length];
        for(int i=0;i<x.length;i++)
            for(int j=0;j<y.length;j++){
                if(x[i] == y[j]){
                    arr[i][j] = true;
                    if(i==0 || j==0)
                        count[i][j] = 1;
                    else {
                        //比较水平、垂直、对角+1位置的最长子集,取最大值
                        count[i][j] = count[i - 1][j - 1] + 1;
                        //记录最长子集最后一个元素位置
                        if (max < count[i][j]) {
                            max = count[i][j];
                            maxI = i;
                            maxJ = j;
                        }
                        if (count[i - 1][j] > count[i][j]) {
                            count[i][j] = count[i - 1][j];
                            arr[i][j] = false;
                        }
                        if (count[i][j - 1] > count[i][j]) {
                            count[i][j] = count[i][j - 1];
                            arr[i][j] = false;
                        }
                    }
                }
                else{
                    arr[i][j] = false;
                    if(i==0 || j==0)
                        count[i][j] = 0;
                    else {
                        count[i][j] = count[i - 1][j - 1];
                        count[i][j] = Math.max(count[i][j], count[i - 1][j]);
                        count[i][j] = Math.max(count[i][j], count[i][j - 1]);
                    }
                }
            }
        //迭代进行字符串拼接GTCGTCGGAAGCCGGCCGAA
        System.out.println(get(arr,count,maxI,maxJ,max,x));
    }
    public static String get(boolean[][] a,int[][] b, int maxI, int maxJ, int max,char[] x){
        for(int i = maxI;i>=0;i--)
            for(int j = maxJ;j>=0;j--) {
                if (a[i][j] && b[i][j] == max) {
                    String s = get(a, b, i - 1, j - 1, max - 1, x);
                    if (s == null)
                        return Character.toString(x[i]);
                    else
                        return String.join("",s, Character.toString(x[i]));
                }
            }
        return null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值