浅谈(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;
}