AcWing 272 最长公共上升子序列 (dp)

题目链接:https://www.acwing.com/problem/content/274/

经典题,首先我们知道最长上升子序列和最长公共子序列的求法,
那么最长上升公共子序列就是把两者的状态结合一下:\(dp[i][j]\) 表示 \(A\) 的前 \(i\) 个字符和 \(B\) 的前 \(j\) 个字符构成的以 \(B[j]\) 结尾的 \(LCIS\)

转移方程即为:

\[dp[i][j] = \begin{cases} dp[i][j] = dp[i-1][j]&A[i] \neq B[j] \\ max_{0\leq k<j,B[k]<B[j]}\{dp[i-1][j] + 1\} & A[i] = B[j]\\ \end{cases} \]

这样直接转移的复杂度是 \(O(n^3)\)

而我们发现,当 \(A[i] = B[j]\) 时,\(B[k] < B[j]\) 的条件可以改写成 \(B[k] < A[i]\), 我们发现转移时,决策集合不会减少, 所以如果记录一下 \(i - 1\) 这一层,\(dp[i-1][j]\) 的前缀满足条件的最大值,就可以做到 \(O(1)\) 转移,时间复杂度就降为 \(O(n^2)\)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 3010;

int n;
int a[maxn], b[maxn];
int f[maxn][maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read();
	for(int i = 1 ; i <= n ; ++i) a[i] = read();
	for(int i = 1 ; i <= n ; ++i) b[i] = read();
	
	for(int i = 0 ; i <= n ; ++i) f[i][0] = 0;
	
	int mx = 0;
	for(int i = 1 ; i <= n ; ++i){
		mx = 0;
		for(int j = 1 ; j <= n ; ++j){
			if(a[i] == b[j]){
				f[i][j] = mx + 1;
			} else{
				f[i][j] = f[i - 1][j];
			}
			if(b[j] < a[i]) mx = max(mx, f[i - 1][j]);
		}
	}
	
	int ans = 0;
	for(int i = 1 ; i <= n ; ++i){
		ans = max(ans, f[n][i]);
	}
	
	printf("%d\n", ans);
	
	return 0;
}
posted @ 2020-12-07 20:59  Tartarus_li  阅读(54)  评论(0编辑  收藏  举报