这道题信息量好大啊
1.Dilworth定理
- Dilworth定理:对于一个偏序集,最少链划分等于最长反链长度。
- Dilworth定理的对偶定理:对于一个偏序集,其最少反链划分数等于其最长链的长度。
其实就是说,对于一个序列,
最大上升子序列长度 = 不上升子序列个数,最大不上升子序列长度 = 上升子序列个数,
最大下降子序列长度 = 不下降子序列个数,最大不下降子序列长度 = 下降子序列个数。
所以这道题:Q1求最大不上升子序列长度,Q2求不上升子序列个数 = 最大上升子序列长度。
2.STL函数:lower_bound( )和upper_bound( )
lower_bound(num,num+L,A)-num; //返回第一个 >=A 的值
upper_bound(num,num+L,A)-num; //返回第一个 >A 的值
lower_bound(num,num+L,A,greater<int>())-num; //返回第一个 <=A 的值
upper_bound(num,num+L,A,greater<int>())-num; //返回第一个 <A 的值
只能在单调序列里调用,从前往后找
lower是>=,upper是>,用greater或者cmp改成<= / <
得到的是元素的地址,最后减去数组的地址就得到了元素下标。
其实就是代替了二分查找...二分的写法见P1439 【模板】最长公共子序列
需要调用<algorithm>库,如果用greater还要调用<iostream>
注意:
1.读入时
while(scanf("%d",&a[++n])!=EOF) {
continue;
}
n--;
因为是先进行++n操作再判断的,所以多了一次,最后要n--.
2.Q1每次要求更小的,所以up1[0]要赋值为INF,不能为0.
代码如下
动态规划( O(n^2),100分 )
#include<cstdio> #include<iostream> using namespace std; const int maxn = 200005; int n,ans,a[maxn],f[maxn],g[maxn]; int main() { while(scanf("%d",&a[++n])!=EOF) { f[n] = 1; g[n] = 1; } for(int i = n; i >= 1; i--) for(int j = i+1; j <= n; j++) if(a[i] >= a[j]) f[i] = max(f[i],f[j]+1); for(int i = 1; i <= n; i++) ans = max(ans,f[i]); printf("%d ",ans); ans = 0; for(int i = n; i >= 1; i--) for(int j = i+1; j <= n; j++) if(a[i] < a[j]) g[i] = max(g[i],g[j]+1); for(int i = 1; i <= n; i++) ans = max(ans,g[i]); printf("%d ",ans); return 0; }
正解( O(nlogn),200分 )
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; const int maxn = 200005; int n,ans,a[maxn],up1[maxn],up2[maxn]; int main() { while(scanf("%d",&a[++n])!=EOF) { continue; } n--; up1[0] = maxn; for(int i = 1; i <= n; i++) { if(a[i] <= up1[ans]) up1[++ans] = a[i]; else { int k = upper_bound(up1+1,up1+ans+1,a[i],greater<int>())-up1; up1[k] = a[i]; } } printf("%d ",ans); ans = 0; up2[1] = a[1]; for(int i = 1; i <= n; i++) { if(a[i] > up2[ans]) up2[++ans] = a[i]; else { int k = lower_bound(up2+1,up2+ans+1,a[i])-up2; up2[k] = a[i]; } } printf("%d ",ans); return 0; }