动态规划常见模型之序列专题

序列专题

动态规划常见序列模型专题有以下几种:

最大子段问题

最长上升子序列
dp[i]表示以i号元素为结尾的最长上升子序列长度

最大公共子序列
dp[i][j]表示s1前i个元素和s2前j元素的公共子列长度

最大子段和问题

最大子段和问题
最大子段和就是所有子段中和最大的
例如:
-2 11 -4 13 - 5 -2
最大子段和就是:11+(-4)+13=20
在这里插入图片描述

package 常见动态规划模型;
/*
测试数据:
6
-2 11 -4 13 -5 -2
答案:
20
*/


import java.util.Scanner;
public class 最大子段和 {
    static int INF=0x7fffffff;//16进制表示的最大整数
    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        int n=reader.nextInt();
        int []num=new int[n+1];
        for(int i=0;i<n;i++) {
            num[i]=reader.nextInt();
        }
        int ans=-INF;
        for(int i=0;i<n;i++) {//如果序列全为非正数 最大子段和为最大值
            ans=Math.max(ans, num[i]);
        }
        if(ans<=0) {//特判掉全为非正数的情况
            System.out.println(ans);
        }else{//序列有正也有负
            int sum=0;
            for(int i=0;i<n;i++) {
                if(sum+num[i]<=0) {
                    sum=0;
                }else {//以i号元素为结尾的序列有正向作用
                    sum=sum+num[i];
                }
                ans=Math.max(ans, sum);//看看是否需要更新ans
            }
            System.out.println(ans);
        }
    }
}

最长上升子序列

在这里插入图片描述
在这里插入图片描述

package 常见动态规划模型;
/*
测试数据:
6
3 2 6 1 4 5
答案:3
6
4 10 4 3 8 9
答案:3
*/
import java.util.Scanner;
public class 最长上升子序列 {
    static int n;
    static int []a;
    static int []dp;
    //dp[i]表示以i号元素结尾的最长上升子序列个数
    //状态方程:dp[i]=max(dp[i],dp[j]+1) (1<=j
    //时间复杂度:O(n^2)
    static int LIS() {
        int ans=0;
        for(int i=1;i<=n;i++){
            
            dp[i]=1;//初值
            for(int j=1;j<i;j++) {
                if(a[j]<a[i]) {//更新为较大者
                    dp[i]=Math.max(dp[i],  dp[j]+1);
                }
            }
            ans=Math.max(ans, dp[i]);
        }
        return ans;
    }
    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        n=reader.nextInt();
        dp=new int[n+1];
        a=new int[n+1];
        for(int i=1;i<=n;i++) {
            a[i]=reader.nextInt();
        }
        System.out.println(LIS());
    }
}

最长公共子序列

dp表
在这里插入图片描述
再举个例子
在这里插入图片描述

package 动态规划;
public class 最长公共子序列 {
    
    /**递归
     * @param nums1
     * @param i 表示nums1数组的前i个
     * @param nums2
     * @param j 表示nums2数组的前j个
     * @return 最长公共子序列的长度
     * 空间复杂度:O(Math.min(m,n) 递归深度*辅助空间
     * 时间复杂度:O(2^n)
     */
    int LongestCommonSubsequence0(String s1,int  i,String s2,int j) {
        if(i==0||j==0||s1==null||s2==null)return  0;
        if(s1.charAt(i-1)==s2.charAt(j-1)) {
            return  LongestCommonSubsequence0(s1,i-1,s2,j-1)+1;
        }
        //返回nums1前i个与nums2前j-1个 和 nums1前i-1个与nums2前j个最长公共子序列的较大者
        return  Math.max(LongestCommonSubsequence0(s1,i,s2,j-1  ),
                LongestCommonSubsequence0(  s1,i-1,s2,j ) );
    }
    
    //动态规划解法
    /**确定状态 dp[i][j] 表示s1的前i个字符与s2的前j个字符的公共子序列长度
     * 递推基 dp[0][j] 和 dp[i][0]都为0
     * @param s1
     * @param s2
     * @return dp[len1][len2]即为s1和s2的最大公共子序列长度
     * 空间复杂度:O(m*n)
     * 时间复杂度:O(m*n)
     *
     */
    int LongestCommonSubsequence1(String  s1,String s2) {
        if(s1==null||s2==null)return 0;
        int m=s1.length();
        int n=s2.length();
        int [][]dp=new int [m+1][n+1];
        for(int i=1;i<=m;i++) {//枚举每一行
            for(int j=1;j<=n;j++) {
                if(s1.charAt(i-1)==s2.charAt(j-1)) {
                    dp[i][j]=dp[i-1][j-1]+1;
                }else {
                    dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        return dp[m][n];
    }
    /**
     * 优化空间复杂度为O(2*(m或n))
     * 使用两行的滚动数组存储状态
     * 字符串为空即 "" 存在对象但length为0
     * 与字符串等于null不一样 为null表示该对象不存在 不能调用字符串的方法
     * @param s1
     * @param s2
     * @return
     */
    int LongestCommonSubsequence2(String  s1,String s2) {
        if(s1==null||s2==null)return 0;
        int m=s1.length();
        int n=s2.length();
        if(m==0||n==0) return 0;
        int [][]dp=new int [2][n+1];
        for(int i=1;i<=m;i++) {
            for(int j=1;j<=n;j++) {
                if(s1.charAt(i-1)==s2.charAt(j-1)) {
                    dp[i%2][j]=dp[(i-1)%2][j-1]+1;
                }else {
                    dp[i%2][j]=Math.max(dp[(i-1)%2][j],  dp[i%2][j-1]);
                }
            }
        }
        return dp[m%2][n];
    }
    int LongestCommonSubsequence3(String  s1,String s2) {
        if(s1==null||s2==null)return 0;
        int m=s1.length();
        int n=s2.length();
        if(m==0||n==0) return 0;
        int []dp=new int[n+1];
        for(int i=1;i<=m;i++) {
            int cur=0;
            for(int j=1;j<=n;j++) {
                int lefttop=cur;
                cur=dp[j];
                if(s1.charAt(i-1)==s2.charAt(j-1)) {
                    dp[j]=lefttop+1;
                }else {
                    dp[j]=Math.max(dp[j-1],  dp[j]);
                }
            }
            
        }
        return dp[n];
    }
    /**
     * 使用一维数组 包含上一行部分dp值和本行部分dp值
     * @param s1
     * @param s2
     * @return
     */
    int LongestCommonSubsequence4(String  s1,String s2) {
        if(s1==null||s2==null)return 0;
        int m=s1.length();
        int n=s2.length();
        if(m==0||n==0) return 0;
        int []dp=new int[n+1];
        for(int i=1;i<=m;i++) {
            int lefttop=0;//换到下一行之后要清零
            for(int j=1;j<=n;j++) {
                if(s1.charAt(i-1)==s2.charAt(j-1)) {
                    //覆盖前先保存dp[j] 该值为下一个的左上
                    int now=dp[j];
                    dp[j]=lefttop+1;
                    lefttop=now;
                }else {
                    lefttop=dp[j];
                    dp[j]=Math.max(dp[j-1],  dp[j]);
                }
            }
            
        }
        return dp[n];
    }
    
    /**
     * 以长度较小者作为dp的长度
     * @param s1
     * @param s2
     * @return
     */
    int LongestCommonSubsequence5(String  s1,String s2) {
        if(s1==null||s2==null)return 0;
        if(s1.length()==0||s2.length()==0)  return 0;
        String rowNums,colNums;
        //s1长度比s2大
        if(s1.length()>s2.length()) {
            rowNums=s1;
            colNums=s2;
        
        }else {
            rowNums=s2;
            colNums=s1;     
        }
        
        int []dp=new int[colNums.length()+1];
        for(int i=1;i<=rowNums.length();i++) {
            int cur=0;
            for(int j=1;j<=colNums.length();j++)  {
                int lefttop=cur;
                cur=dp[j];
                if(rowNums.charAt(i-1)==colNums.charAt(j-1)) {
                    dp[j]=lefttop+1;
                }else {
                    dp[j]=Math.max(dp[j-1],  dp[j]);
                }
            }
            
        }
        return dp[colNums.length()];
    }
    public static void main(String[] args) {
        String s1="BA34C";
        String s2="A1BC2";
        int len1=s1.length();
        int len2=s2.length();
        最长公共子序列 a=new 最长公共子序列();
        System.out.println(a.LongestCommonSubsequence0(s1,len1,s2,len2));
        System.out.println(a.LongestCommonSubsequence1(s1,s2));
        System.out.println(a.LongestCommonSubsequence2(s1,s2));
        System.out.println(a.LongestCommonSubsequence3(s1,s2));
        System.out.println(a.LongestCommonSubsequence4(s1,s2));
        System.out.println(a.LongestCommonSubsequence5(s1,s2));
    }
}

你可能感兴趣的:(#,蓝桥杯算法学习,算法,动态规划)