动态规划常见序列模型专题有以下几种:
最大子段问题
最长上升子序列
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));
}
}