十分重要的基础DP问题,是学习用各种方式优化DP的一个很好的例子。
设DP[i]表示以a[i]结尾的LIS的长度,则状态转移方程为
DP[1]=1
DP[i] = max{DP[j], j<i && a[j]<a[i]} + 1, i>1
暴力求解复杂度为O(N^2)。
优化1
考虑函数f(L):长度为 L 的 Increasing Sequence (IS) 的最小结尾, 显然f(L)是单调递增的。
从左到右扫描数组,维护动态的f(L)。
再看上面的DP方程,考虑我们维护的这个函数f(L)如何加速DP状态转移时所需的查询。
显然我们以a[i]为key,二分查询f(L),得到max{L, f(L)<a[i]}
复杂度降为O(N*logN)
这一类DP优化方法可归纳为 加速DP Query
#include<cstdio> #include<algorithm> using namespace std; const int MAX_N=1e6+10, oo=1e6; int A[MAX_N], f[MAX_N]; //二分法: binary_search(y, f()) //设有一个单调(不要求严格单调)的函数f() //可以在O(log N)的时间内求解以下问题: //给定y,求满足f(x)<=y/f(x)>=y的最大/最小的x // int binary_search(int y, int l, int r){ //y is the key int mid; while(r-l>1){ //r is illegal mid=(l+r)>>1; if(f[mid]<y){ l=mid; } else r=mid; } return l; } int main(){ //freopen("in", "r", stdin); int N, ans=0; scanf("%d", &N); for(int i=1; i<=N; i++) scanf("%d", A+i); f[0]=0; for(int i=1; i<=N; i++) f[i]=oo; for(int i=1; i<=N; i++){ int tmp=binary_search(A[i], 0, ans+1)+1; ans=max(ans, tmp); f[tmp]=min(f[tmp], A[i]); } printf("%d\n", ans); return 0; }
优化2
时间: 2024-10-10 16:43:05