zoukankan      html  css  js  c++  java
  • 洛谷【AT2827】LIS

    浅谈(DP)https://www.cnblogs.com/AKMer/p/10437525.html

    题目传送门:https://www.luogu.org/problemnew/show/AT2827

    (LIS)就是(Longest) (Increasing) (Subsequence),最长上升子序列问题。

    给你一个序列(a),要你求最长上升子序列的长度。

    我们可以把序列看做是一个个插数到序列尾部形成的,那么每插入一个新的数字,就是一个新的阶段。我们可以考虑用这个新插入的数字来做上升子序列的结尾,这个上升子序列会有多长。由于每次插入新的数字我们要做的事情都是一样的,所以这个问题就具有子问题重复性。由于我用这个新的数字结尾并不会影响到以以前数字结尾的子序列的长度,所以这个问题满足无后效性。并且,以当前这个数字为结尾的子序列的长度可以由在它之前的比它小的数字结尾的子序列长度得到,所以这个问题满足最优子结构性。于是我们就可以用(DP)来做了。

    我们可以设立状态(f_i)表示以第(i)位数字结尾的上升子序列最长的长度是多少。一共有(n)个阶段,每次决策选择在(i)之前的比(a_i)小的数字来更新(f_i),状态转移方程就是:(f_i=max(f_j+1)(0leqslant j<i,a_j<a_i))(f)初始均为(0),最后求出整个数组的最大值即为答案。

    时间复杂度:(O(n^2))

    空间复杂度:(O(n))

    代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e3+5;
    
    int n,ans;
    int a[maxn],f[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    int main() {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	for(int i=1;i<=n;i++) {
    		for(int j=0;j<i;j++)
    			if(a[j]<a[i])
    				f[i]=max(f[i],f[j]+1);
    		ans=max(ans,f[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    由于(f_i)只会由(f_j+1)更新得到((j)是满足(x<i)(a_x<a_i)(f_x)最大的(x)),那么我们完全没有必要枚举没有用的位置,那样对于时间的浪费就太大。所以我们可以对于这个方程进行改进。

    (f_i)为以数字(i)结尾的最长上升子序列的长度。那么(f_i=max(f_j+1)(0leqslant j <i)),因为我们的阶段还没有进行到(i)后面去,所以(f_j)表示的必然是(i)前面的某一个数字(j)结尾的上升子序列长度。这样子我们就可以用树状数组来维护(f)数组,每次查询前缀最大值(+1)来更新当前的(f_{a_i}),然后再在树状数组上改动相应的位置即可。如果序列的值域一开始不在([1,n]),那么我们离散化即可。

    时间复杂度:(O(nlogn))

    空间复杂度:(O(n))

    代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define low(i) ((i)&(-(i)))
    
    const int maxn=1e5+5;
    
    int n,cnt;
    int tmp[maxn],a[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    struct tree_array {
    	int c[maxn];
    
    	void change(int pos,int v) {
    		for(int i=pos;i<=cnt;i+=low(i))
    			c[i]=max(c[i],v);
    	}
    
    	int query(int pos) {
    		int res=0;
    		for(int i=pos;i;i-=low(i))
    			res=max(res,c[i]);
    		return res;
    	}
    }T;
    
    int main() {
    	n=read();
    	for(int i=1;i<=n;i++)
    		tmp[i]=a[i]=read();
    	sort(tmp+1,tmp+n+1);
    	cnt=unique(tmp+1,tmp+n+1)-tmp-1;
    	for(int i=1;i<=n;i++) {
    		a[i]=lower_bound(tmp+1,tmp+cnt+1,a[i])-tmp;
    		int f=T.query(a[i]-1)+1;
    		T.change(a[i],f);
    	}
    	printf("%d
    ",T.query(n));
    	return 0;
    }
    
  • 相关阅读:
    Django的路由寻址
    Guava Cache 原理分析与最佳实践
    「必知必会」最细致的 ArrayList 原理分析
    「必知必会」最细致的 LinkedList 原理分析
    oracle listagg() 函数
    .net HttpClient 向 WebApi Post 的方法
    MySql 如何让表名支持大小写
    Java的基本数据类型
    CF666E
    spring使用mybatis执行SQL脚本,创建和初始化数据库
  • 原文地址:https://www.cnblogs.com/AKMer/p/10437536.html
Copyright © 2011-2022 走看看