NOIP 1999 普及 导弹拦截 O(nlogn)

题意

求一个序列的最长不上升子序列, 和它的最长上升子序列.

思想

以最长不上升子序列为例.

维护当前找到的不上升子序列的当前位置的最大值, 以下简称: 子序列数组. 每扫到一个新数, 替换序列中小于它的最大元素, 如果整个序列都大于等于它, 就新开一位, 成为更长的子序列. 而每次将一个数加入序列后, 一定存在一个和当前插入的元素位置等长的序列, 所以答案就是将元素都插入后序列的长度.

单调性

首先, 不难看出, 这种操作维护的序列一定是单调的. 如果新加一个元素, 说明当时所有序列尾都大于它, 所以最后加入的元素最小. 对于一个更新了的元素来说, 它更新后一定不会大于上一个元素, 也一定不会小于等于下一个元素. 综上, 每次操作完子序列数组单调.

正确性

对于序列数组中每一个元素, 它出现之前, 这里一定出现过一个比它小的数 (或者没有数, 即新开的一位), 根据单调性, 可知加入的元素前一位的数大于等于它, 而且一定比它靠前 (不然扫都没扫到). 而每一位上的元素之所以在这个位, 一定是因为它前面一位存在过一个出现比它早而且大于等于它的数.

所以, 以子序列数组中的某个元素为序列尾, 长度等于其下标的不上升子序列一定存在. 由此可得, 当前最长不上升子序列长度就等于当前的子序列数组长度.

优化

因为数组单调, 如果用二分查找优化, 每个元素加入仅需 O(logn), 总复杂度 O(nlogn).

实现

由于一开始没输入 n, 所以用特殊的读入方式.

二分查找就用自带的 lower_boundupper_bound 函数即可.

放代码:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <vector>
using namespace std;
int n, m, a[100005] /*原序列*/, b[100005] /*子序列尾*/, ans = 0;
int main() {
  n = 0;
  while (cin >> a[++n]) {
  }
  --n;
  b[0] = 1;                               // b数组长度为 1
  b[1] = a[1];                            //第一个子序列当前的序列尾
  for (register int i(2); i <= n; ++i) {  //扫一遍, 求最长不上升子序列
    if (b[b[0]] >=
        a[i]) {  //如果有一个元素比当前子序列数组中最小的都小, 新开一位
      b[++b[0]] = a[i];
    } else {
      *upper_bound(b + 1, b + b[0] + 1, a[i], greater<int>()) =
          a[i];  //查找 b 中第一个小于等于当前元素的位置, 替换
    }
  }
  printf("%d\n", b[0]);
  memset(b, 0, sizeof(b));
  b[0] = 1;
  b[1] = a[1];
  for (register int i(2); i <= n; ++i) {  //进行最长上升子序列的查找
    if (b[b[0]] < a[i]) {  //道理都一样, 判断规则稍微改变
      b[++b[0]] = a[i];
    } else {
      *lower_bound(b + 1, b + b[0] + 1, a[i]) = a[i];
    }
  }
  printf("%d\n", b[0]);
  return 0;
}
posted @   Wild_Donkey  阅读(117)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示