算法分析与设计--动态规划

文章目录

  • 一、动态规划简介
  • 二、动态规划求解步骤
  • 三、动态规划典型应用
    • 数字三角形问题
    • 最大子段和问题
    • 0-1背包问题
  • 四、最长公共子序列问题
    • 动态规划求解
  • 五、总结


前言

算法语言--java语言


一、动态规划简介

动态规划算法通常用于求解具有某种最优性质的问题。动态规划与分治算法类似,其基本思想也是将待求解的问题分解成若干个子问题,再把子问题合成一个最优解。

动态规划与分治法的区别:

分治法子问题相互独立,动态规划子问题不相互独立

动态规划问题应具备两个基本要素:

1、最优子结构性质,2、子问题重叠性质

二、动态规划求解步骤

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

(1)分析最优子结构性质

(2)递归地定义最优值

(3)以自底向上的方式计算出最优值

(4)根据计算最优值时得到的信息,构造最优解

三、动态规划典型应用

1、数字三角形问题

有一个群岛,共分为若干层,第1层有一个岛屿,第2层有2个岛屿,......第n层有n个岛屿。每个岛上都有一块宝,其价值是一个正整数(图中圆圈中的整数)。

寻宝者只允许从第一层的岛屿进入,从第i层的岛屿退出,不能后退,他能收集他所经过的所有岛屿上的宝贝。但是,从第i层的岛屿进入第i+1层的岛屿时,有且仅有有2条路径。你的任务是:对于给定的群岛和岛上宝贝的价值,计算一个寻宝者行走一趟所能收集宝贝的最大价值。


 问题分析:

 这个问题抽象成三角形问题,从顶部出发,从顶至底选一条路径,使该路径经过的数字总和最大。

本题采用贪心算法就无法找到真正的最大和。当从顶部向下,使用贪心算法时,路径上的数字和为:9+15+8+9+10=51。而真正的最大和为9+12+10+18+10=59。因此本题不能使用贪心算法求最大和。

视频讲解如下链接: 

https://www.bilibili.com/video/BV1Z94y1D7CA/?spm_id_from=333.337.search-card.all.click&vd_source=4db006022c06ce1bdafdbea04cd95cb0

动态规划法求解:

public class Main {
    public static void main(String[] args) {
    int n = 3;
        Scanner in = new Scanner(System.in);
        int[][] data = new int[n+2][n+2];
    for (int i=1;i<=n;i++){
        for (int j =1;j<=i;j++){
            int temp = in.nextInt();
            data[i][j] = MaxNum(data[i-1][j-1],data[i-1][j])+temp;//本层数加上一层两者最大的数
        }
      }
    
    //最底层的列比较大小
    int result = 0;
    for (int i=1;i<=n;i++){
        if (result<data[n][i]){
            result = data[n][i];
        }
    }
        System.out.println("最大结果为:"+result);
    }
    static int MaxNum(int a,int b){
        if (a>b){
            return a;
        }else {
            return b;
        }
    }
}

 运行如下:


2、最大子段和问题

最大子段和问题:给定n个元素的整数列(可能为负整数)a1,a2......an。求其和的最大值。

例如当(a1,a2,a3,a4,a5,a6)= (-2,11,-4,13,-5,-2),最大子段和为11-4+13=20

现在求当(a1,a2,a3,a4,a5,a6)=(11,-2,-4,13,-5,-2),最大子段和为多少?

问题分析:

动态规划求最大子段和比分治算法要简单得多!

 如子段和是大于0,则加上下一个数,如果小于0,则直接舍弃前面,加下一个数。

代码如下:

public class Main {
    public static void main(String[] args) {
        int data[] = {11,-2,-4,13,-5,-2};
        int max = MaxSum(data,6);
        System.out.println("最大子段和为:"+max);
    }
    static int MaxSum(int data[],int n){
        int a = 0;
        int max = 0;
        for (int i = 0; i < n; i++) {
            if(a>0){
                a = a+data[i];//做累加
            }else{
                a = data[i];//直接舍弃前面
            }
            if(a>max){
                max = a;
            }
        }
        return max;
    }
}

运行如下: 


3、 0-1背包问题

给定一个物品集合S = {1,2,3...,n},物品i的重量是wi,其价值是vi,背包的容量为W,即最大载重量不能超过W。在限定的总重量W内,我们如何选择物品,才能使物品的总价值最大。

0-1背包问题样例数据
W=5物品1物品2物品3物品4
重量w2132
价值v12102015

填写最优值解析:

 解法归纳:
一、如果装不下当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
二、如果装得下当前物品。
假设1:装当前物品,在给当前物品预留了相应空间的情况下,前n-1个物品的最佳组合加上当前物品的价值就是总价值。
假设2:不装当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
选取假设1和假设2中较大的价值,为当前最佳组合的价值。

0-1背包问题样例数据的最优值构造过程
物品\背包容量012345
物品1:w=20012121212
物品2:w=101012222222
物品3:w=301012223032
物品4:w=201015253037

讲解视频:

 【动态规划】背包问题_哔哩哔哩_bilibili

代码实现:

public class Text {
    public static void main(String[] args) {
        int w[] = {0,2,1,3,2};//物品重量
        int v[] = {0,12,10,20,15};//物品价值
        int bagMax = 5;//背包大小
        int dp[][] = new int[5][6];//背包问题表格
        for (int i=1;i<=4;i++){
            for (int j=1;j<=bagMax;j++){
                if(j<w[i]){
                    dp[i][j] = dp[i-1][j];
                }else {
                    dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
                }
            }
        }

        //表格的打印
        for (int i = 0;i<5;i++) {
            for (int j =0;j<6;j++){
                System.out.print(dp[i][j]+"\t\t");
            }
            System.out.println();
        }
    }
    static int max(int x,int y){
        if (x>y){
            return x;
        }else {
            return y;
        }
    }
}

运行如下:


四、最长公共子序列问题

1、问题描述:

著名的最长公共子序列问题,即寻找序列间最大或最多公共点的问题。最长公共子序列算法的主要作用是找出两个序列中最长的公共子序列,是一种非常基础的算法,被广泛应用于程序相似性对比、图形相似处理解、计算生物学等方面。

生物学家常常利用该算法进行基因序列对比,由此推测序列的结构、功能和演化过程。

最长公共子序列问题的定义为:设有两个序列X[1:m]和Y[1:n],需要寻找它们之间的一个最长公共子序列。

例如,假设我们有如下两个序列:

X:A B C B D A B

Y:   B D C A B A

则X和Y的最长公共子序列的长度为:4

则X和Y的一个最长公共子序列为:B C B A

2、动态规划求解 

利用动态规划寻找最长公共子序列的步骤如下:

(1)先寻找最长公共子序列的长度。

(2)再通过算法从而获得最长公共子序列

根据题目得到的递推公式为: 

 

 3、代码实现

public class Text {
    static final int max = 100;
    static int[][] c = new int[max][max];
    static int[][] b = new int[max][max];

    //长度的动态规划算法
    public static int LCSLength(int m,int n,char x[],char y[]){
        for (int i=1;i<=m;i++){
            c[i][0] = 0;
        }
        for (int i=1;i<=n;i++){
            c[0][i] = 0;
        }
        for (int i = 1; i <=m; i++) {
            for (int j = 1; j <=n; j++) {
                if(x[i]==y[j]){
                    c[i][j] = c[i-1][j-1]+1;
                    b[i][j] = 1;
                }else if(c[i-1][j]>=c[i][j-1]){
                    c[i][j] = c[i-1][j];
                    b[i][j] = 2;
                }else if(c[i-1][j]<c[i][j-1]){
                    c[i][j] = c[i][j-1];
                    b[i][j] = 3;
                }
            }
        }
        return c[m][n];
    }

    //打印最长子序列
    public static void LCSL(int i,int j,char x[]) {
        if(i==0||j==0) {
            return;
        }
        if (b[i][j]==1) {
            LCSL(i - 1, j - 1, x);
            System.out.print(x[i]);
        }else if (b[i][j]==2){
            LCSL(i-1,j,x);
        }else{
            LCSL(i,j-1,x);
        }
    }
    public static void main(String[] args) {
        char x[] = {'0','A','B','C','B','D','A','B'};
        char y[] = {'0','B','D','C','A','B','A'};
        int max = LCSLength(x.length-1,y.length-1,x,y);
        System.out.println("最长公共子序列的长度为:"+max);
        System.out.print("最长公共子序列为:");
        LCSL(x.length-1,y.length-1, x);
    }
}

 


五、总结

动态规划和分治法十分类似,主要区别为:

分治法子问题相互独立,动态规划子问题不独立。

可以使用一个表来记录所有已解的子问题的答案。

拥有最优子结构性质和子问题重叠性质的题考虑用动态规划求解。

动态规划问题需要多思考多做题才能熟能生巧,得慢慢看视频慢慢懂。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值