本题考查
动态规划问题(LCS算法),因为题目要求元素可重复,所以判断赋值的状态方程与原始LCS有区别
动态规划介绍
- 动态规划的核心就是:根据之前已经解决的子问题的解来求解父问题,换句话说“先解决子问题,再由子问题解决父问题”
- 动态规划实例:《算法导论》钢管切割问题
- 详细的动态规划介绍可以参考https://blog.csdn.net/u013309870/article/details/75193592
LCS最长公共子序列问题介绍
从两个序列A与B中找到最长的子序列,如A<1,2,3,4,5,6>、B<3,5,6>,则AB最大的公共序列为<3,5,6>。
使用动态规划方法求解LCS最长公共子序列的核心就是理解下式:
解析:Xi为X序列中第i个元素,Yj为Y序列中第j个元素。两个序列的LCS问题包含两个序列的前缀的LCS,因此,LCS问题具有最优子结构性质。
设C[i,j]表示Xi和Yj的最长公共子序列LCS的长度。
- 如果i=0或j=0,即一个序列长度为0时,那么LCS的长度为0。
- 如果Xi = Yj,则代表C[ i,j ]等于先前的公共序列长度加1,先前的公共序列即C[i - 1,j - 1]
- 如果Xi ≠ Yj,则代表C[i,j]等于C[ i,j - 1]与C[i - 1,j ]两者中较大的一个
参考资料:
- https://blog.csdn.net/so_geili/article/details/53737001
- https://blog.csdn.net/v_JULY_v/article/details/6110269
代码
最后一个用例超时,C++代码不会超时
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int[][] dp = new int[205][10005];
Scanner scaner = new Scanner(System.in);
scaner.nextInt();
int round1 = scaner.nextInt();
int[] order = new int[round1];
for(int i = 0 ; i < round1 ; i++) {
order[i] = scaner.nextInt();
}
int round2 = scaner.nextInt();
int[] color = new int[round2];
for(int i = 0 ; i < round2 ; i++) {
color[i] = scaner.nextInt();
}
scaner.close();
for(int i = 1 ; i <= round1 ; i++) {
for(int j = 1 ; j <= round2 ; j++) {
if(order[i - 1] == color[j - 1])
//元素不能重复版本
// dp[i][j]=dp[i-1][j-1]+1;
//元素可以重复版本
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+1;
else
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
System.out.println(dp[round1][round2]);
}
}
需要注意!!!
本题元素可重复,所以状态方程与原始LCS算法有区别
如果不能重复等于同一个元素
if (order[i] == color[j])
dp[i][j] = dp[i-1][j-1] +1;
else
dp[i][j] = Math.max(dp[i][j-1] , dp[i-1][j])
如果可以重复等于
if (order[i] == color[j])
dp[i][j] = max(dp[i-1][j] , dp[i][j-1]) +1;
else
dp[i][j] = Math.max (dp[i-1][j] ,dp[i][j-1])
参考:https://blog.csdn.net/Cute_jinx/article/details/81911913
最后把dp矩阵非零部分输出出来便于理解运行过程,该矩阵使用的用例是PAT标准用例1
6
5 2 3 1 5 6
12 2 2 4 1 5 5 6 3 1 1 5 6
//dp矩阵
1 2 2 2 2 2 2 2 2 2 2 2
1 2 2 2 2 2 2 3 3 3 3 3
1 2 2 3 3 3 3 3 4 5 5 5
1 2 2 3 4 5 5 5 5 5 6 6
1 2 2 3 4 5 6 6 6 6 6 7