LIS最长上升子序列
一种方法是O(Nˆ2)的DP
主要是O(NlogN)的DP
最长上升子序列,最长不下降子序列,最长下降子序列,最长不上升子序列都同理
以LIS最长上升子序列为例
DP思路:
总的来说,我们要求以 i为结尾,最长的上升子序列的长度
现在到了第 i 个数,那么前面的 1~i-1的数都已经处理完毕了
设 f[x] 表示以 长度x的上升子序列中,子序列末端的最优值 ,此处f[x]就是保持最小值
比如 1,2,3,4 (5) 现在到了5,1~4都已经处理完毕,则长度为 3 的上升子序列的最优值是 3,不是4,f[3]=3
那么这有个巧妙的结论,f[x]一定是单调递增的,理解理解
1~i-1都已经处理完毕,对于 i 结尾的最长上升子序列,我们要找到最后一个小于 a[i] 的 f[x],那么对于 i 结尾的最长上升子序列的长度就是 x+1;
这过程可用二分处理,然后更新f[x+1],使得f[x+1]始终保持最优值。
ans=0; memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { if(!ans) { ans=1,f[1]=a[1];continue; } int l=1,r=ans; while(l+1<r) { int mid=(l+r)/2; if(f[mid]<a[i]) l=mid; else r=mid; } int len; if(f[r]<a[i]) len=r; else if(f[l]<a[i]) len=l; else len=0; if(!f[len+1]) f[len+1]=a[i]; else f[len+1]=min(f[len+1],a[i]); if(len+1>ans) ans=len+1; } printf("%d\n",ans);
那么以最长不上升子序列为例,f[x]是单调递减的,我们要找最后一个大于等于 a[i] 的 f[x]
f[x]要保持最优解,就是始终保持最大值。
int ans=0; for(int i=1;i<=n;i++) { if(!ans) { ans=1,f[1]=a[1];continue; } int l=1,r=ans; while(l+1<r) { int mid=(l+r)/2; if(f[mid]>=a[i]) l=mid; else r=mid; } int len; if(f[r]>=a[i]) len=r; else if(f[l]>=a[i]) len=l; else len=0; f[len+1]=max(f[len+1],a[i]); if(len+1>ans) ans=len+1; } printf("%d\n",ans);
例题:
P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目简化:
给定一个数列 b,问:
- 它的最长不上升子序列长度;
- 最少能被划分成多少个不上升子序列。
1.就不说了
2. Dilworth 定理 :最少的不上升子序列的个数就是最长上升子序列的长度 (记住就好)