zoukankan      html  css  js  c++  java
  • 4.17 DP专题:最长上升子序列+导弹拦截

    最长上升子序列长度+输出答案序列

    题意

    设有(n)个不相同的整数组成的序列(a[1cdots n]),且(a[i]!=a[j](i!=j)),若存在(i_1<i_2<cdots i_e),且有(a[i_1]<a[i_2]<cdots <a[i_e]),称为长度为(e)的上升序列。
    求出最长上升序列的长度及这个最长上升序列。

    题解

    由于要记录这个最长的上升序列,那么(nlog{n})的非dp做法似乎不可行,用(n^2)dp来写。
    首先,(dp[i])记录从(1)(i)的最长上升序列长度,设其初值是1(pre[i])记录以(a[i])为结尾的子序列的前一个元素位置。
    考虑如何更新(dp)
    可以列举(i),外层循环中找到以(i)为结尾的最大序列。
    内层循环中:枚举(j,jin [1,i));

    得到如下做法:

    状态转移方程:

    • 如果(a[j] < a[i]),代表当前的(a[i])可以成为已知的以(a[j])结尾的最长序列的最新一位元素(得到新的序列为(a[dots],a[dots],a[j],)(a[i]))。
      • (dp[i] = max(dp[i],dp[j]+1));
      • 此时如果更新了(dp[i])的值,那么代表得到的序列是目前最新的,长度最长的序列:
        • (i)的前一位是(j),表示为(pre[i]=j);
        • 记录下当前(i)的位置,表示为(ans=i);
        • (maxn)记录序列最长长度。
      • 这里可以优化成:如果 (a[j] < a[i] quad&&quad dp[i] < dp[j] + 1)
        最后输出长度(maxn)

    对于输出答案序列,使用递归:

    • 先找print(ans),表示从最后一位开始输出,
    • 一直向(pre[x])递归下去,直到(x=0),表示找到头,回溯。
    • 每次回溯时候输出答案即可。

    特判一下如果maxn的值没有被更新,则未发现最长序列,直接特殊输出即可。



    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f , N = 1e5+5; 
    int a[N],pre[N],n,dp[N],ans,maxn = -INF;
    void print(int x){
    	if(!x)return;
    	print(pre[x]);
    	printf("%d ",a[x]);
    }
    signed main(){
    	while(scanf("%d",&a[++n]) != EOF);
    	n--;
    	
    	for(int i = 1 ; i <= n ; i ++)
    		dp[i] = 1;
    	for(int i = 1 ; i <= n ; i ++)
    		for(int j = 1 ; j < i ; j ++)
    			if(a[j] < a[i] && dp[i] < dp[j] + 1){
    					dp[i] = dp[j] + 1,
    					pre[i] = j;
    					if(maxn < dp[i]){
    						maxn = dp[i],
    						ans = i;
    					}
    				}
    	if(maxn == -INF){
    		printf("max=%d
    %d",1,a[1]);
    		return 0;
    	}
    	printf("max=%d
    ",maxn);
    	print(ans);	
    	
    	return 0;
    }
    



    导弹拦截

    题意

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
    输入导弹依次飞来的高度(雷达给出的高度数据是(le50000)的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

    题解

    对于第一问,找到该序列中的最长不上升子序列即可。
    对于第二问,找到该序列中的“最长不下将子序列”即可。


    以第一问为例,(O(n^2))的思路及算法同前一题,不再赘述。
    这里提供一个(O(nlog n))的求最长不下降子序列的算法:
    开一个数组(q),表示一个“单调栈”,维护可行的最长序列。
    由于我们并不关心这个最长序列是什么,所以只需维护数组的长度即可。
    对于新加入的元素(设为(b),判断:

    • 如果(b)>栈顶元素,那么直接把它放入栈顶即可。
    • 否则((b)小于等于栈顶元素):
      • 在当前栈内找到第一个比(b)大或等于的元素(lower_bound),将此元素的值更改为(b)
        这样执行,最后得到的栈的长度即为答案。
        一些小问题:
    1. 为什么它可行:
      因为维护的是当前序列单调不下降的栈, 改变其中一个值不会影响长度;在后面加上的数一定是满足所有计入栈内元素的不下降的一个。
    2. 为什么这样得到的数组不是答案数组
      在更新长度之后,如果修改一个元素,就不是成为答案的最长的一个了。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f , N = 1e5+5; 
    int a[N],q1[N],q2[N],cnt,n;
    signed main(){
    	while(scanf("%d",&a[++n]) != EOF);
    	n--;
    	q1[++cnt] = a[1];
    	for(int i = 2 ; i <= n ; i ++){
    		if(a[i] <= q1[cnt]) q1[++cnt] = a[i];
    		else {
    			int l = 1 , r = cnt;
    			while(l < r){
    				int mid = (l + r) >> 1;
    				if(q1[mid] < a[i]) r = mid;
    				else l = mid + 1;
    			}
    			q1[l] = a[i];
    		}
    	}
    	printf("%d
    ",cnt);
    	
    	cnt = 0;
    	q2[++cnt] = a[1];
    	for(int i = 2 ; i <= n ; i ++){
    		if(a[i] > q2[cnt])q2[++cnt] = a[i];
    		else {
    			int l = 1 , r = cnt;
    			while(l < r){
    				int mid = (l + r) >> 1;
    				if(q2[mid] >= a[i]) r = mid;
    				else l = mid + 1;
    			}
    			q2[l] = a[i];
    		}	
    	}
    	printf("%d",cnt);
    	
    	return 0;
    }
    
  • 相关阅读:
    ubuntu 14.04+apache 反向代理设置
    ubuntu 14.04 使用apt-get出现如下问题解决办法
    ubuntu 出现 Unable to locate package update 解决办法
    在ubuntu 12.04 apache 限制IP访问的方法
    (原创)在ubuntu 14.04 中安装Apache2+modsecurity+awstats (新手教程)
    windows 10 的安装说明
    前端三大主流框架中文文档
    关于移动端影像配置了https之后拍出来的照片在android手机无法显示的问题
    ES5常用api
    promise循环调用异步函数(以图片上传为例)
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/14671149.html
Copyright © 2011-2022 走看看