题意


有若干发导弹袭来,由于咱们的导弹系统比较拉,当系统打掉一个导弹之后,系统就只能打到与这个导弹高度一样或者低于这个高度的导弹。
第一个问题问你这个系统最多能打掉多少个导弹。
第二个问题问你最少需要多少个系统能把所有的导弹都给打下来。


思路


翻译一下题意就是让咱们找到最长的单调递减子序列,我们可以用a[]存每颗导弹的高度,用q[i]来表示此时长度i的单调递减子序列的最后一个元素,用反证法可证,q[i] > q[j](i < j)。
然后我们从前往后遍历每颗导弹,用len记录当前最长的单调递减子序列的长度,用二分来确定这颗导弹可以插入在长度为多少的序列尾端,把这颗导弹插入到q的该位置中,即可解答第一问。
第二问是用贪心的方法来做,由于我们需要把所有导弹都给打下来,所以每遇到一颗导弹,就一定要把它给打下来。
我们用f[cnt]表示第cnt套系统能够打到导弹的最高高度,当cnt为0时,代表没有系统,于是增加一套系统。如果该导弹高度大于当前的这套系统,则判断下一套系统能否击落,若能,则令这套系统的f[i]等于这颗导弹的高度,若不存在系统,则添加一个新的系统。 另外值得一提的是,题目输入没有给出导弹的具体数量,只是给了一行导弹数据,这样我们可以用到头文件中的stringstream ssin(string)来处理数据,先用字符串line读取一整行的数据,然后用stringstream ssin(line),之后即可用ssin对数组进行输入。


#include <iostream>
#include <algorithm>
#include <sstream>
using namespace std;

const int N = 1e5 + 7;

int a[N], q[N], f[N];
int n;

int main()
{
  ios::sync_with_stdio(false);
  string line;
  getline(cin, line);
  stringstream ssin(line);
  while (ssin >> a[n]) n++;
  
  int len = 0, cnt = 0;
  for (int i = 0; i < n; i++)
  {
      int l = 0, r = len;
      while (l < r)
      {
          int mid = l + r + 1 >> 1;
          if (q[mid] >= a[i]) l = mid;
          else r = mid - 1;
      }
      len = max(len, r + 1);
      q[r + 1] = a[i];
      
      int k = 0;
      while (k < cnt && f[k] < a[i]) k++;
      if (k == cnt) f[cnt++] = a[i];
      else f[k] = a[i];
  }
  
  cout << len << endl;
  cout << cnt << endl;
  
  return 0;
}