洛谷P1020 导弹拦截(求最长上升子序列和最长非上升子序列---二分查找)

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式

输入格式:
1行,若干个整数(个数≤100000)

输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入样例#1:
389 207 155 300 299 170 158 65

输出样例#1:
6
2

思路:
第一个问就是很明显求最长的非上升子序列的长度,而第二个问题是求最长上升子序列的长度,要用到Dilworth定理,还要一种证明是这么证明的:
洛谷P1020 导弹拦截(求最长上升子序列和最长非上升子序列---二分查找)_第1张图片可直接利用STL的库函数:
lower_bound()和upper_bound()实现二分查找,回忆一下二者的用法:
一、当数组为升序时:
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

一、当数组为降序时,需要重载函数:
lower_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int maxn = 100000 + 5;

int down[maxn];
int up[maxn];

int main()
{

	
	int num;
	scanf("%d", &num);
	down[1] = up[1] = num;
	int didx = 1;
	int uidx = 1;
	while (~scanf("%d", &num)) {
		if (num > up[uidx])
			up[++uidx] = num;
		else if (num < up[uidx]) {
			int l = 1;
			int r = uidx;
			while (l <= r) {
				int m = l + (r - l) / 2;
				if (up[m] >= num)
					r = m - 1;
				else
					l = m + 1;
			}
			up[l] = num;
			//int p = lower_bound(up+1, up+ uidx+1, num) - up; 从左往右找出第一个大于等于num的数,-up返回数组下标
			//up[p] = num;
		}

		if (num <= down[didx])
			down[++didx] = num;

		else {
			int l = 1;
			int r = didx;
			while (l <= r) {
				int m = l + (r - l) / 2;
				if (down[m] < num)  //注意这里不能有等于
					r = m - 1;
				else
					l = m + 1;
			}			
			down[l] = num;
			//int p = upper_bound(down+1, down + didx+1, num, greater()) - down; 从左往右找出第一个大于num的数,-down返回数组下标
            //down[p] = num;
		}

	}	
	cout << didx << endl << uidx;
			
	return 0;
}

你可能感兴趣的:(二分查找,洛谷题解)