:root { --bg-color: #ffffff; --text-color: #333333; --header-bg: #f5f5f5; } .night-mode { --bg-color: #1a1a1a; --text-color: #e0e0e0; --header-bg: #2d2d2d; } body { background: var(--bg-color); color: var(--text-color); } .header { background: var(--header-bg); }

题解:UVA12511 Virus

题意

给定两个长度分别为 \(n,m\) 的序列 \(a,b\),求这两个序列的最长公共递增子序列。

思路

最长公共递增子序列是最长上升子序列与最长公共子序列的结合,那么就需要将两者的思路相结合。

首先设计状态 \(dp_{i,j}\) 表示序列 \(a\) 的前 \(i\) 个组成的子序列与序列 \(b\) 的前 \(j\) 个组成的子序列的最长公共递增子序列。然后分类讨论。

\(a_i \ne b_j\),则不能算贡献,且由于序列以 \(b_j\) 结尾,所以只能继承 \(dp_{i-1,j}\),得转换方程:

\[dp_{i,j}\gets dp_{i-1,j} \]

\(a_i=b_j\),则需找到一个 \(k\) 满足 \(1\le k <j\)\(b_k<a_i\),满足 \(dp_{i-1,k}\) 最大,然后算上一个 \(1\) 的贡献。当然,也可以选择继承 \(dp_{i-1,j}\),这里需要对两者取最大值。得转换方程:

\[dp_{i,j}\gets\max( \max_{1\le k<j,b_k<a_i}\{dp_{i-1,k}+1\},dp_{i-1,j}) \]

注意答案不一定是 \(dp_{n,m}\),故需要一个变量来记录所有状态的最大值。

于是可以写出以下代码:

	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 1 ; j <= m ; j ++) {
			int maxx = 1;
			if(a[i] == b[j]) {
                for(int k = 1 ; k < j ; k ++) {
				if(a[i] > b[k]) maxx = max(maxx , dp[i - 1][k] + 1);
			    }
            dp[i][j] = max(dp[i - 1][j] , maxx);    
            } else dp[i][j] = dp[i - 1][j];     
            ans = max(ans , dp[i][j]);
		}
	}

时间复杂度为 \(O(nm^2)\),所以超时也是自然的,现在考虑如何优化。

不难发现 \(k\) 这一层循环是没必要的,因为它遍历的区间 \([1,j-1]\) 在之前就已经遍历过了,所以再设计一个新的状态一起转移。

定义状态 \(dp_{i,j,0}\) 表示上述的最长公共递增子序列,\(dp_{i,j,1}\) 表示满足 \(1\le k <j\)\(b_k<a_i\) 的最大 \(dp_{i-1,k,0}\)

然后按原本的思路分类讨论。

\(a_i \ne b_j\),转换方程为:

\[dp_{i,j,0}\gets dp_{i-1,j,0} \]

\(a_i=b_i\),转换方程为:

\[dp_{i,j,0}\gets\max(dp_{i,j-1,1}+1,dp_{i-1,j,0}) \]

\(a_i>b_j\),转换方程为:

\[dp_{i,j,1}\gets\max(dp_{i,j-1,1},dp_{i-1,j,0}) \]

\(a_i\le b_j\),转换方程为:

\[dp_{i,j,1}\gets dp_{i,j-1,1} \]

按照上述思路就可以得到以下代码:

	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 1 ; j <= m ; j ++) {
			if(a[i] == b[j]) {
            dp[i][j][0] = max(dp[i - 1][j][0] , dp[i][j - 1][1] + 1);    
            } else dp[i][j][0] = dp[i - 1][j][0];     
			if(a[i] > b[j]) dp[i][j][1] = max(dp[i][j - 1][1] , dp[i - 1][j][0]);
            else dp[i][j][1] = dp[i][j - 1][1];
            ans = max(ans , dp[i][j][0]);
		}
	}

于是我们就成功地把时间复杂度从 \(O(nm^2)\) 优化成了 \(O(nm)\)

时间复杂度看起来无法继续优化,于是我们尝试优化空间复杂度。

不难发现,\(dp_{i,j,1}\) 是由 \(dp_{i,j-1,1}\) 转移而来,只与上一个状态有关,可以用滚动数组滚成零维即一个变量。

再来看 \(i\) 这一维,它同样只与上一个状态有关,所以也可以用滚动数组滚掉,于是就只剩下了 \(dp_j\)

最终空间复杂度就由 \(O(nm)\) 优化成了 \(O(m)\)

得到以下代码:

	for(int i = 1 ; i <= n ; i ++) {
		int maxx = 0;
		for(int j = 1 ; j <= m ; j ++) {
			if(a[i] == b[j]) {
                dp[j] = max(dp[j] , maxx + 1);    
			    ans = max(ans , dp[j]);
            }  
			if(a[i] > b[j]) maxx = max(maxx, dp[j]);
		}
	}

似乎无法继续优化了,这道题也就做完了。

全代码

#include<bits/stdc++.h>
#define int long long
#define I_love_Foccarus return
#define cin_fast ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define deap(i,a,b) for(int i=a;i>=b;i--)
#define endl '\n'
//#define getchar getc
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
#define fi first
#define se second
#define pd(a) push_back(a)
#define inui(a) a = read_unsigned_int()
#define in(a) a = read_int()
#define inp(a , b) a = read_int() , b = read_int()
#define inl(a) a = read_long_long()
using namespace std;
const int Size = 1 << 14;
const int N = 3e3 + 5;
const int inf = 0x3f3f3f3f;
inline char getc() {
	static char syn[Size] , *begin = syn , *end = syn;
	if(begin == end) begin = syn , end = syn + fread(syn , 1 , Size , stdin);
	I_love_Foccarus *begin ++;
}
inline int read_unsigned_int() {
	int x = 0;
	char ch = getchar();
	while('9' < ch || ch < '0') ch = getchar();
	while('0' <= ch && ch <= '9') x = (x << 3) + (x << 1) + ch - '0' , ch = getchar();
	I_love_Foccarus x;
}
inline int read_int() {
	int x = 0;
	char ch = getchar();
	bool f = 0;
	while('9' < ch || ch < '0') f |= ch == '-' , ch = getchar();
	while('0' <= ch && ch <= '9') x = (x << 3) + (x << 1) + ch - '0' , ch = getchar();
	I_love_Foccarus f ? -x : x;
}
inline int read_long_long() {
	long long x = 0;
	char ch = getchar();
	bool f = 0;
	while('9' < ch || ch < '0') f |= ch == '-' , ch = getchar();
	while('0' <= ch && ch <= '9') x = (x << 3) + (x << 1) + ch - '0' , ch = getchar();
	I_love_Foccarus f ? -x : x;
}
int a[N] , b[N] , dp[N];
signed main() {
	//cin_fast;
    int t;
    in(t);
    while(t --) {
	memset(dp , 0 ,sizeof(dp));
	int n;
	in(n);
	for(int i = 1 ; i <= n ; i ++) in(a[i]);
    int m;
    in(m);
	for(int i = 1 ; i <= m ; i ++) in(b[i]);
    int ans = 0;
	for(int i = 1 ; i <= n ; i ++) {
		int maxx = 0;
		for(int j = 1 ; j <= m ; j ++) {
			if(a[i] == b[j]) {
                dp[j] = max(dp[j] , maxx + 1);    
			    ans = max(ans , dp[j]);
            }  
			if(a[i] > b[j]) maxx = max(maxx, dp[j]);
		}
	}
	cout<<ans<<'\n';
    }
	I_love_Foccarus 0;
}
posted @ 2025-07-31 13:10  雨落潇湘夜  阅读(12)  评论(0)    收藏  举报
我的页脚图片