最长公共上升子序列(未完成)

最长上升子序列

【题目描述】:
给定一个长度为\(N\)的数列,求数值严格单调递增的子序列的长度最长是多少

第一阶段

\(1\leq N \leq 1000\)
做法:我们可以考虑直接暴力求解。
\(Code\):

/*
 by : Zmonarch 
 知识点 ; 最长上升子序列
 做法: force 
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define int long long 
using namespace std ; 
const int kmaxn = 1e6 + 10 ;
inline int read() 
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
} 
int a[kmaxn] , n ;
int f[kmaxn] ;   
signed main()
{
	n = read() ; 
	for(int i = 1 ; i <= n ; i++) a[i] = read() ; 
	for(int i = 1 ; i <= n ; i++) 
	{
		f[i] = 1 ; 
		for(int j = 1 ; j < i ; j++) 
		{
			if(a[i] > a[j]) 
			{
				f[i] = max(f[i] , f[j] + 1) ; 
			}
		}
	}
	int ans = 1 ; 
	for(int i = 1 ; i <= n ; i++) ans = max(f[i] , ans) ; //最长上升子序列不一定以n结尾 
	printf("%lld\n" , ans) ; 
	return 0 ; 
}

第二阶段

\(1\leq N \leq 10^6\)
我们发现\(N\)有点大,上方\(O(N^2)\)做法会\(TLE\),为了防止这种情况的出现,我们考虑优化,显然\(O(N logN)\)对于\(10^6\)是可过的,那么我们可以考虑进行数据结构优化。那么我们也可以仔细的观察一下状态转移
\(f_{i} = max(f_j + 1 , f_i)\),
这个式子比较好,我看不出来任何单调性,所以考虑树状数组暴力平摊\(logN\)的复杂度去寻找\(f_j\)的最大值,

对于数据结构优化\(DP\),不需要什么脑子,全是套路,可以尝试着理解这种套路,自然也可以直接背过。
\(Code\):

/*
 by : Zmonarch
 知识点 ; 最长上升子序列
 做法: 树状数组优化 
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define int long long
#define lowbit(x) x&(-x)
using namespace std ;
const int kmaxn = 1e6 + 10 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n , a[kmaxn] , f[kmaxn] ;
int v[kmaxn] ;   
void update(int x , int val) 
{
	for(int i = x ; i <= n ; i += lowbit(i)) 
	{
		f[i] = max(f[i] , val) ;
	}
}
int query(int x ) 
{
	int ans = 0 ; 
	for(int i = x ; i ; i -= lowbit(i)) 
	{
		ans = max(ans , f[i]) ; 
	}
	return ans ; 
}
signed main()
{
	n = read() ; 
	int ans = 0 ; 
	for(int i = 1 ; i <= n ; i++) a[i] = read() , v[i] = a[i] ; 
	sort(v + 1 , v + n + 1 ) ; 
	int len = (v + 1 , v + n + 1 ) - v ; 
	for(int i = 1 ; i <= n ; i++) 
	{
		int p = lower_bound(v , v + len , a[i]) - v + 1 ; 
		ans = max(ans , query(p - 1) + 1) ;
		update(p , query(p - 1) + 1 ) ; 
	}
	printf("%lld\n" , ans) ; 
	return 0 ;
}

第三阶段

【题目描述】:
给定一个序列,初始为空。
现在我们将 \(1\)\(N\) 的数字插入到序列中,每次将一个数字插入到一个特定的位置。
每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
【输入格式】:
第一行一个整数 \(N\),表示我们要将 \(1\)\(N\) 插入序列中。

第二行 \(N\) 个数字,第 \(k\) 个数字 \(X_k\),表示我们将 k 插入到位置 \(X_k\)

\(solution\)】:
考查点:平衡树+线段树,笔者不会平衡树

最长公共子序列

给定两个长度分别为\(N\)\(M\)的字符串\(A\)\(B\),求既是\(A\)的子序列又是\(B\)的子序列的字符串长度最长是多少。

第一阶段

\(n,m\leq 1000\)
发现\(n,m\)非常小,考虑直接\(force\)

/*
 by : Zmonarch
 知识点 ; 最长上升子序列
 做法: force
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std ;
const int kmaxn = 1e3 + 10 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n , m ; 
int f[kmaxn][kmaxn] ; 
char a[kmaxn] , b[kmaxn] ; 
signed main()
{
	n = read() , m = read() ;
	cin >> a + 1 ;
	cin >> b + 1 ;  
	/*for(int i = 1 ; i <= n ; i++) a[i] = read() ; 这里也是可以转化为
	//int的,一样
	//处理
	for(int j = 1 ; j <= n ; j++) b[j] = read() ;*/
	for(int i = 1 ; i <= n ; i++) 
	{
		for(int j = 1 ; j <= m ; j++) 
		{
			if(a[i] == b[j]) 
			{
				f[i][j] =  f[i - 1][j - 1] + 1 ; 
			}
			else 
			{
				f[i][j] = max(f[i - 1][j] , f[i][j - 1]) ;
			}
		}
	}
	printf("%lld\n" , f[n][m] ) ; 
	return 0 ;
}

第二阶段

\(description\)】:
字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。
令给定的字符序列 \(X=x_0x_1…x_{m-1}\),序列 \(Y=y_0y_1…y_{k-1}\)\(X\) 的子序列,存在 \(X\) 的一个严格递增下标序列 \(i_0,i_1,…,i_{k-1}\) ,使得对所有的 \(j=0,1,…,k-1\),有 \(x_{i_j} = y_j\)
例如,\(X=ABCBDAB\)\(Y=BCDB\)\(X\) 的一个子序列。
对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。
输入格式
\(1\) 行为第 \(1\) 个字符序列,都是大写字母组成,以 . 结束。
\(2\) 行为第 \(2\) 个字符序列,都是大写字母组成,以 .结束。
注意,两个字符序列均不包含最后的 .。
输出格式
\(1\) 行输出上述两个最长公共子序列的长度。
\(2\) 行输出所有可能出现的最长公共子序列个数,答案可能很大,只要将答案对 \(100,000,000\) 求余即可。
数据范围
输入字符序列的长度都不超过 \(5000\)

对于此题,和上方的第三个阶段一样,笔者不会。 等会了的话,会来更新的。

最长公共上升子序列

\(description\)】:
对于两个数列\(A\)\(B\),如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了

数列$A$和数列$B$的长度均不超过$3000$.

解法1

\(force\)
首先我们可以先考虑出是否是公共子序列,然后看一下是否是上升的子序列,为啥先判断公共子序列呢?
可以看一下我们的判断。
1.先判断公共子序列

if(a[i] == b[j]) 
{
   …… …… 
   if(a[l] < a[i]) 
   {
     …… ……
   }
}

2.先判断上升子序列

if(a[i] < a[j] && b[i] < b[j]) 
{
		 ……
	 if(a[l] == b[r]) 
	 {
		 ……
	 }
}

然后你发现,第一种打字少。
那么其他细节也就不赘述了
\(Code\)】:

/*
 by : Zmonarch
 知识点 ; 最长公共上升子序列
 做法: force
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std ;
const int kmaxn = 3e3 + 10 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n ; 
int a[kmaxn] , b[kmaxn] ;  
int  f[kmaxn][kmaxn] ; 
signed main()
{
	n = read() ; 
	for(int i = 1 ; i <= n ; i++) a[i] = read() ; 
	for(int j = 1 ; j <= n ; j++) b[j] = read() ; 
	for(int i = 1 ; i <= n ; i++) 
	{
		for(int j = 1 ; j <= n ; j++) 
		{
			if(a[i] == b[j])  
			{
				for(int l = 1 ; l <= i ; l++) 
				{
					for(int r = 1 ; r <= j ; r++) 
					{
						if(a[l] < a[i]) 
						{
							f[i][j] = max(f[i][j] , f[l][r] + 1) ; 
						}
					}
				}
			}
		}
	}
	int ans = 0 ; 
	for(int i = 1 ; i <= n ; i++) 
	{
		for(int j = 1 ; j <= n ; j++) 
		{
			ans = max(ans , f[i][j]) ; 
		}
	}
	printf("%lld\n" , ans) ; 
	return 0 ;
}

理由的话,就先放一下,然后今晚上时间不多了 ,就只打码了

解法2

我们显然有一个三维的\(DP\)

/*
 by : Zmonarch
 知识点 ; 最长上升子序列
 做法: force
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std ;
const int kmaxn = 1e6 + 10 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n ; 
int a[kmaxn] , b[kmaxn] ; 
signed main()
{
	n = read() ; 
	for(int i = 1 ; i <= n ; i++) a[i] = read() ; 
	for(int j = 1 ; j <= n ; j++) b[j] = read() ; 
	int ans = 0 ; 
	for(int i = 1 ; i <= n ; i++) 
	{
		for(int j = 1 ; j <= n ; j++) 
		{
			if(a[i] != b[j]) 
			{
				f[i][j] = f[i - 1][j] ;
			}
			else 
			{
				for(int k = 0 ; k < j ; k++) 
				{
					if(a[k] < a[i]) 
					{
						f[i][j] = max(f[i][j] , f[i - 1][k] + 1) ; 
					}
				}
			}
		}
		ans = max(ans , f[i][j]) ; 
	}
	printf("%lld\n" , ans) ;
	return 0 ;
}

解法3

\(O(n^2)\)显然应该可以过掉这道题了

/*
 by : Zmonarch
 知识点 ; 最长上升子序列
 做法: force
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
//#define int long long加上就MLE
using namespace std ;
const int kmaxn = 3e3 + 10 ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n , a[kmaxn] , b[kmaxn] , ans ; 
int f[kmaxn][kmaxn] ; 
signed main()
{
	n = read() ; 
	for(int i = 1 ; i <= n ; i++) a[i] = read() ;
	for(int j = 1 ; j <= n ; j++) b[j] = read() ;
	for(int i = 1 ; i <= n ; i++)
	{
		int maxv = 1 ; 
		for(int j = 1 ; j <= n ; j++)
		{
			f[i][j] = f[i - 1][j] ; 
			if(a[i] == b[j]) f[i][j] = max(f[i][j] , maxv) ; 
			if(a[i] > b[j]) maxv = max(maxv , f[i - 1][j] + 1) ;
		}
	}
	for(int i = 1 ; i <= n ; i++) 
	{
		ans = max(f[n][i] , ans) ;
	}
	printf("%d\n" , ans) ; 
	return 0 ;
}

posted @ 2021-02-06 21:45  SkyFairy  阅读(170)  评论(0编辑  收藏  举报