• 【题解】P1020 导弹拦截


    【题解】P1020 导弹拦截

    从n^2到nlogn

    第二问就是贪心,不多说
    第一问:

    简化题意:求最长不下降子序列

    普通n^2:

    for (int i = 1; i <= n; i++)
        for (int j = 1; j < i; j++)
            if(a[j] >= a[i])
                f[i] = max(f[i], f[j] + 1);
    cout << f[n];
    

    另一种n^2级,可能快一点点(还没交,不知对不对)

    f[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        if(a[i] == 0)
            continue;
        f[a[i]] = f[a[i] - 1] + 1;
        for (int j = a[i]; j <= max_ai; j++)
            f[j] = max(f[j], f[a[i]]);
    }
    cout << f[max_ai] << endl;
    

    接下来就是大佬讲的nlogn

    题解(@w1049344862)
    首先是upper_bound() 和lower_bound()的用法
    然后是神奇的栈操作

    w1049344862曰:

    O(nlogn)求出最长不上升子序列的长度
    (即一套系统最多拦截数)

    1.实现方式
    首先我们需要一个数组a,存储从第1个到第n个导弹的高度

    然后一个数组d(其实是个栈),存储不上升序列

    把a中的每个元素挨个加到d里面:

    (a中第i个元素为a[i],d长度为len,d中最后一个(也是最小的一个)为d[len])

    如果a[i] <= d[len],说明a[i]可以接在d后面(而整个d还是有序的),那就简单粗暴地把a[i]丟进d:
    d[ ++len ] = a[i]
    如果a[i] > d[len],说明a[i]接不上
    但是我们发扬瞎搞精神:接的上要接,接不上创造条件也要接!

    强行把a[i]塞进去:
    在d中找到第一个小于a[i]的数,把它踹了,用a[i]代替它!(为什么正确在下面)

    假设这个数是y,怎样踹掉它呢?

    很明显,我们需要使用lower_bound和upper_bound来查找

    第一步,找一个听起来无比正确的理由,比如它占着位置不干活啦,干起活来还不如a[i]啦,naive啦,它too young啦,too simple啦......反正能骗过lower_bound和upper_bound就行

    (lower_bound&&upper_bound:你当我们傻)(w1049:真聪明)

    接下来,特别有正义感的lower_bound和upper_bound就会去把y给拎出来

    第二步,考虑使用什么

    我们知道,要求的是最大不上升子序列长度,也就是如果两个元素相等也是可以的

    所以我们踹人就不用踹等于a[i]的了

    结合上面,应该使用upper_bound(终于想起来它了)并且使用>作为比较器(这是个下降序列)

    第三步,直接开搞

    int p = upper_bound(d + 1, d + 1 + len, a[i], greater () ) - d;

    d[p] = a[i];
    成功把a[i]塞了进去

    2.为什么正确
    显然成立

    如果y在末尾,由于y < a[i],所以y后面能接的不如a[i]多,y让位给a[i]可以让序列更长

    如果y不在末尾,那y有生之年都不会再被用到了,直接踹了y就行,y咋样,who care?

    注意到lower_bound只能在有序序列中使用,此时d还有序吗?

    当然有序。(本文第一个句号)

    假设y前一个y1,y后一个是y2,则

    y1 > y > y2

    因为y是第一个小于a[i]的,所以

    y1 > a[i]

    又因为

    a[i] > y > y2

    所以

    y1 > a[i] > y2

    对比下原来的式子

    y1 > y > y2

    a[i]可以完美代替y,至于y以后咋办,who care?

    对于最长上升子序列,只需要把上面的过程通通换一下符号

    可以用以下方法证明:

    反之亦然同理,推论自然成立,略去过程QED,由上可知证毕(多么美妙的证明)
    3.代码:
    for(int i=2;i<=n;i++)
    if(d[len]>=a[i])d[++len]=a[i];
    else {
    int p=upper_bound(d+1,d+1+len,a[i],greater())-d;
    d[p]=a[i];
    }
    最后len就是要求的最大不上升子序列长度

    但要注意的是,d中存储的并不是最大不上升子序列!

    原因如下:

    即得易见平凡,仿照上例显然,留作习题答案略,读者自证不难

    以上是洛谷题解)哈哈,诙谐的作业确让人思考

    为啥len=答案??

    每加入一个,肯定是经比较满足最长不下降子序列后才扩栈的。
    也就是在第i个元素时,len一旦+1,前提都是存在以a[i]结尾的且长为len的最长不下降子序列。
    你想,更新栈内元素,使小的被较大的覆盖,并不改变原来元素所构成的序列的存在性。(这就是正确性)
    而这次更新的操作,是为了贪心得到更优解(当然不是让栈内成为新的序列(栈内本来就不是个序列))。
    下次有元素,加到大点的元素后面比加到小点的元素后面更优。为啥??
    他的作用体现出来就是更新到栈顶时,栈顶元素变大了,自然就更有可能多捞一个比它小的新元素。

    AC代码

    #include <cmath>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <stack>
    #define N 100010
    #define F(i,a,b)	for(int i = a; i <= b; i++)
    #define UF(i,a,b)	for(int i = a; i >= b; i--)
    using namespace std;
    int a[N], n, len, d[N], max_ai, ans;
    int high[50010];
    int main()
    {
    	while(cin >> a[++n])
    		max_ai = max(max_ai,a[n]);
    	n--;
    	d[++len] = a[1];
    	F(i,2,n){
    		if(a[i] <= d[len])
    			d[++len] = a[i];
    		else{
    			int p1=upper_bound(d+1,d+1+len,a[i],greater<int>())-d;//upper_bound() 本来是找大于a[i]的,此找小于a[i]的,利用greater
    			d[p1] = a[i]; 
    		}
    	}
    //	F(i,1,len)	cout << d[i] << " ";
    //	cout << endl;
    	cout << len << endl;
    	F(i,1,n){
    		int t = 0;
    		F(j,a[i],max_ai){
    			if(high[j]){
    				high[j] --;
    				high[a[i]] ++;
    				t = 1;
    				break;
    			}	
    		}
    		if(!t){
    			high[a[i]] ++;
    			ans ++;
    		}
    	}
    	cout << ans << endl;
    	return 0;
    }
    
    

    总结

    写博客方面:
    1.如果是给别人看,语言要有逻辑顺序,应该对铺垫知识先行处理。即使写给自己,这也是个好习惯。
    2.短句
    3.轻松诙谐
    最重要还是学了upper_bound()和upper_bound()(仅仅是知道)
    更重要是了解了这巧妙的思想

  • 相关阅读:
    jupyter notebook 和 opencv
    AMD 处理器 Ubuntu 16.04 LTS 配置 opencv、caffe 小结
    caffe 在 windows 使用
    MATLAB数字图像处理
    游记(5)
    POJ-1789-Truck History 解题报告
    POJ-2560-Freckles 解题报告
    POJ-1308-Is It A Tree? 解题报告
    POJ-1182-食物链 解题报告
    POJ-1861-Network 解题报告
  • 原文地址:https://www.cnblogs.com/ZhengkunJia/p/12250848.html
Copyright © 2020-2023  润新知