老规矩上图
我每天坚持看美女,不为了啥,只是为了我心情愉悦,嘻嘻嘻嘻
单调子序列关于长度的四种问题
本次主要是dilworth定理在子序列问题的应用,至于想要更深层次的理解可以退出了
Dilworth定理,一言以蔽之,偏序集能划分成的最少的全序集个数等于最大反链的元素个数
对子序列问题来说,就是最长原链长度=反链最小划分数,但何为反链尼,
也就是最长上升子序列的长度 等于不上升子序列的最小划分数
导弹拦截https://www.luogu.com.cn/problem/P1020
#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N =100010;
int a[N];
int f[N],t[N];
int n;
int main(){
while(scanf("%d",&a[++n])!=EOF)continue;
n--;
// cin>>n;
// for(int i=1;i<=n;i++)cin>>a[i];
int l,r,mid;
f[0]=0x3f3f3f3f;
int res=0;
for(int i=1;i<=n;i++){
if(f[res]>=a[i]){//最长不上升子序列的长度
f[++res]=a[i];
}else{
l=0;r=res;
while(l<r){
/*
最长不上升子序列的长度:
res表示当前最长不上升子序列的长度
我们遍历所有的点:
1, 遇到不大于最长不上升子序列的末尾值的话,最长上升子序列的长度加1,同时保存最小的末尾值(f[++res]=a[i])
2,如果大于最长不上升子序列的末尾值的话:
我们需要找到第一个不大于它的位置,并更新它的值(这里,我们需要解释下,为啥要跟新,就是f[i]他表示从0到i的最长上升子序列的长度,)
*/
mid=(l+r)/2;//
if(a[i]<=f[mid])l=mid+1;
else r=mid;
}
if(l!=0)f[l]=a[i];
}
}
cout<<res<<endl;
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i]>t[cnt]){//本来求不上升子序列的最小划分数
t[++cnt]=a[i];//转化为最长上升子序列的长度
}else{
l=0;r=cnt;
while(l<r){
mid=(l+r)/2;
if(t[mid]>=a[i])r=mid;
else l=mid+1;
}
t[l]=a[i];
}
}
cout<<cnt<<endl;
return 0;
}
dilworth定理的应用还是挺广泛的,后期还会更新这篇的博客的,我写东西为了记住当时自己的一些想法,如果这些能帮到你,不胜感激,同时有错误,欢迎指正