动态规划01

1. 什么是动态规划?与递归有什么区别?

先说递归:递归——程序调用自身,也就是函数自己调用自己。递归通常从顶部将问题分解,通过解决掉所有分解出来的小问题,来解决整个问题;

斐波那契数列 f(n) = f(n-1) + f(n-2)   从上往下开始算   从n开始   直至找到n=1 n=2的递归出口

动态规划——通常与递归相反,其从底部开始解决问题。将所有小问题解决掉,进而解决的整个问题。

 

2、动态规划(Dynamic Programming)

一、基本概念

    动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

二、基本思想与策略

    基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

    由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

    与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。

什么题适合用DP解?

使用动态规划特征: 
1. 求一个问题的最优解 
2. 大问题可以分解为子问题,子问题还有重叠的更小的子问题 
3. 整体问题最优解取决于子问题的最优解(状态转移方程) 
4. 从上往下分析问题,从下往上解决问题 
5. 讨论底层的边界问题

DP 一般用一个数组或者二维数组  存储每一次递归过程 或子问题产生的结果

1、构造问题所对应的过程。

2、思考过程的最后一个步骤,看看有哪些选择情况。

3、找到最后一步的子问题,确保符合“子问题重叠”,把子问题中不相同的地方设置为参数。

4、使得子问题符合“最优子结构”。

5、找到边界,考虑边界的各种处理方式。

6、确保满足“子问题独立”,一般而言,如果我们是在多个子问题中选择一个作为实施方案,而不会同时实施多个方案,那么子问题就是独立的。

7、考虑如何做备忘录。

8、分析所需时间是否满足要求。

9、写出转移方程式。

1. 剑指offer   ---剪绳子

给你一根长度为N的绳子,请把绳子剪成M段(m,n都是整数 都大于1),每段绳子的 
长度记为k[0],k[1],k[2]…. 请问如何剪绳子使得k[0],k[1],k[2] 
的乘积最大 
例如 绳子长度8 最大乘积18 = 2*3*3

分析: 求最优  有没有重叠的子问题 ,先剪一刀 i  n-i  (n-i这部分还可以再分)

f(n) = max( f(i)*f(n-i)  ) 这里f(n)长度为n所求的最大乘积  显然需要一个数组去存储 max里面的结果 选择最佳(不同的i对应不同的结果 需要遍历取不同的i)

一般先从最下面开始比如 n=0 n=1 这里 n>1  n=2

代码如下:

public class Main {

    public static int getMax(int length){
        if(length < 2){
            return 0;
        }
        if(length==2) return 1;
        if(length==3) return 2;

        int dp[]=new int[length+1];

        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        int max=0;
        for(int i=4; i <= length; i++){
            max=0;
            for(int j=1; j <= i/2; j++){
                int tmp=dp[j]*dp[i-j];
                if(max < tmp){
                    max=tmp;
                }
                dp[i]=max;
            }
        }

        max=dp[length];
        return max;
    }

    public static void main(String[] args) {
        System.out.println(getMax(8));
    }
}

 

2. 最长递增子序列

给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8},长度为4.

分析:

设长度为N的数组为{a0,a1, a2, ...an-1),则假定以aj结尾的数组序列的最长递增子序列长度为L(j),则L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是说,我们需要遍历在j之前的所有位置i(从0到j-1),找出满足条件a[i]<a[j]的L(i),求出max(L(i))+1即为L(j)的值。最后,我们遍历所有的L(j)(从0到N-1),找出最大值即为最大递增子序列。时间复杂度为O(N^2)。

j=0  aj=5  只有一个       L0=1

j=1  aj=6 aj-1=5  5 <6   L1=2

j=2                                L2=3

j=3     1比前面的小      L3=1 重新开始

j=4   aj=2                     L4=2  //1 2 

j=5    aj=8 比前面大    L5=L2+1=4 // L2是最大的

代码如下:先定义dp数组并赋初值, 用于存放得到的 Lj   

                   从1开始类似于选择排序(从0开始不需要比较 最长子序列就是1),外层遍历1-len

                  内层从 0 --- j-1   比如 5 2 6  3 7 4 8  aj=6的时候     需要与比较5 2比较  

public class Main02 {
    public static int lis(int arr[], int len){
        int dp[]=new int[len];

        for(int i=0; i < len; i++){
            dp[i]=1;
        }

        for(int j=1; j<len; j++){
            for(int i=0; i < j; i++){
                if(arr[j] > arr[i] && dp[j] <dp[i]+1){
                    dp[j]=dp[i]+1;
                }
            }
        }
        int max=0;
        for(int j=0; j<len; j++){
            System.out.print(dp[j]+"");

            if(dp[j]>max){
                max=dp[j];
            }
        }
        System.out.println();
        return max;

    }

    public static void main(String[] args) {
        int arr[]={5,6,7,1,2,8};

        System.out.println(lis(arr,arr.length));
    }
}

3. 最长公共子序列

子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串

  • cnblogs
  • belong

比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与母串保持一致,我们将其称为公共子序列。最长公共子序列(Longest Common Subsequence,LCS),顾名思义,是指在所有的子序列中最长的那一个。子串是要求更严格的一种子序列,要求在母串中连续地出现。在上述例子的中,最长公共子序列为blog(cnblogs,belong),最长公共子串为lo(cnblogs, belong)。

如何求:详细的看这一篇https://www.cnblogs.com/hapjin/p/5572483.html

还是要写下最长公共子序列的递归式才完整。

c[i,j]表示:(x1,x2....xi) 和 (y1,y2...yj) 的最长公共子序列的长度。(是长度哦,就是一个整数嘛

代码如下:

根据得到的递推式进行求解

定义存储数组时的长度要注意下  尽可能长一点0-len   长度为len+1, 直接定义为c[1000][1000]也是一样的

public class TestLcs {
    public static int LCS(String str1, String str2){
        int len1=str1.length();
        int len2=str2.length();

        int [][]c=new int[len1 + 1][len2 +1];

        for(int i=0; i <= len1; i++){
            for(int j=0; j <= len2; j++){
                if(i==0 || j==0){
                    c[i][j]=0;
                }else if(str1.charAt(i-1) == str2.charAt(j-1)){
                    c[i][j]= c[i-1][j-1] +1;
                }else{
                    c[i][j]= Math.max(c[i-1][j], c[i][j-1]);
                }

            }
        }
        return c[len1][len2];
    }
    public static void main(String[] args) {
        String str1 = "abcdeaafgacb";
        String str2 = "acbfg";//acfg
        System.out.println(LCS(str1, str2));
    }
}

4. 最长公共子串(这个不会的话就比较麻烦)

(1)子序列要连续

DP求解最长公共子串
子串是一种特殊的子序列,因此同样可以用DP来解决。定义数组的存储含义对于后面推导转移方程显得尤为重要,糟糕的数组定义会导致异常繁杂的转移方程。考虑到子串的连续性,将二维数组c[i][j]用来记录具有这样特点的子串——结尾同时也为为串x1x2⋯xi与y1y2⋯yj的结尾——的长度。
得到转移方程:


最长公共子串的长度为 max(c[i,j]), i∈{1,⋯,m},j∈{1,⋯,n}。

代码如下所示

public class TestCommonStr {
    public static int CommonStr(String str1, String str2){
        int len1=str1.length();
        int len2=str2.length();
        int result=0;

        int c[][]=new int[len1 +1][len2 +1];
        for(int i=0; i <= len1; i++){
            for(int j=0; j <= len2; j++){
                if(i==0 || j==0){
                    c[i][j]=0;
                }else if(str1.charAt(i-1)== str2.charAt(j-1)){
                    c[i][j]=c[i-1][j-1]+1;
                    result= Math.max(c[i][j], result);
                }else{
                    c[i][j]=0;
                }
            }
        }
        return result;
    }
    public static void main(String[] args) {
        String str1 = "abcdeaafgacb";
        String str2 = "acbfg";//
        System.out.println(CommonStr(str1, str2));//3
    }
}

5 .怎么找出这个公共子串?

直接给出代码如下

public class FIndSonStr {
    public static String findSonStr(String str1, String str2){
        int len1=str1.length();
        int len2=str2.length();

        String strTmp=str1;
        int maxLen=0;
        int index=0;
        String str="";

        int c[][]=new int[len1 +1][len2 +1];

        for(int i=0; i <= len1; i++){
            for(int j=0; j <= len2; j++){
                if(i==0 || j==0){
                    c[i][j]=0;

                }else if(str1.charAt(i-1)== str2.charAt(j-1)){
                    c[i][j]=c[i-1][j-1]+1;


                }else{
                    c[i][j]=0;
                }

                if(c[i][j]>maxLen){
                    maxLen=c[i][j];
                    index=i;
                }
            }
        }

        // charAt()返回的是char   char+char=int
        for(int k=index-maxLen; k < index; k++){
               char ch=strTmp.charAt(k);
               str+=String.valueOf(ch);
        }
            return str;


    }
    public static void main(String[] args) {
        String str1 = "abcdeaafgacb";
        String str2 = "acbfg";//
        System.out.println(findSonStr(str1, str2));//acb
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值