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


    最长上升子序列

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

    第一阶段

    (1leq 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
    " , ans) ; 
    	return 0 ; 
    }
    

    第二阶段

    (1leq 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
    " , ans) ; 
    	return 0 ;
    }
    
    

    第三阶段

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

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

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

    最长公共子序列

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

    第一阶段

    (n,mleq 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
    " , 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
    " , 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
    " , 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
    " , ans) ; 
    	return 0 ;
    }
    
    
  • 相关阅读:
    Celery
    windows笔记目录
    Linux笔记目录
    python笔记目录
    rsa
    c#目录
    webpack3.x配置
    RabbitMQ服务安装(Linux)
    JavaScript验证用户输入
    IP地址检测工具
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/14383135.html
Copyright © 2020-2023  润新知