题目描述

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

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

输入输出格式

输入格式:

111行,若干个整数(个数≤100000 \le 100000≤100000)

输出格式:

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

输入输出样例

输入样例#1: 复制

389 207 155 300 299 170 158 65
输出样例#1: 复制

6
2

题解:

这道题之前的数据是n方的复杂度都可以过,但是在洛谷上面要nlogn的复杂度才可以,这里先讲第一种

第一问:

就是用平常的的最长上升子序列的模板

 1 for(int i=n; i>=1; --i)
2 {
3 dp[i]=1;
4 for(int j=n; j>i; --j)
5 {
6 if(v[j]<=v[i] && dp[j]+1>dp[i])
7 dp[i]=dp[j]+1;//printf("%d %d %d %d\n",v[j],v[i],j,i);
8 }
9 maxx=max(maxx,dp[i]);
10 }

但是要注意这个dp中的dp[i]表示从起始位置到i这个位置的数组长度中的最长上升子序列

例如:

1 4 3 6 5 8      这个序列

dp[1]是代表   1  这个序列的LIS

dp[2]是代表   1 4 这个序列

...............

但是要注意这个dp[i]中的LIS一定包含第i个数(可以说最后一位已经确定)

这就导致了长度为w的序列的LIS不一定放在dp[w]中

格外注意:      要在dp[初]------dp[w]中取最大值

第二问(最长上升子序列就行)之后又证明:

这个就可以用贪心算法来解决

假设给出的序列为v[]

先初始化一个空序列dp[]   和一个一直记录它的长度 len

先把v中的第一个元素放到dp中,len初始化为1

从v的第二位开始如果大于dp[len],那我们就必须把它追在再dp数组的后面,因为这个时候按照题意必须新开一个系统

注意:这个dp序列中的值是递增的,在之后的解释中会发现,由此便知道dp[len]是他的最大值,如果发射的最大高度小于v中得值,那就必须新开一个系统(解释上一句)

如果这个值小于dp[len],那就证明我们之前的导弹系统可以拦截到他,那我们就要更新之前的导弹系统的高度

为了弄成一个递增序列,我们要 从dp(头)------dp(len)来搜索一把,找到第一个,把他的之改成现在这个

为什么弄成递增序列,因为可以用二分来降低复杂度,而且这只是顺带的操作

而且它既然是递增序列了,那就证明我们取的第一个比它大的值,不是特别大,这样也做到了最优

例如:

dp序列中有了 1    3    5    7

我们现在这个v的值是4

那我们按我们最有思想肯定是要更新5而不是7,因为7可以为了防止6的出现而新增一个系统(这个举例为了说清楚上面那一句话)

操作起来就是两个判断

全部代码:

 1 #include<stdio.h>
2 #include<string.h>
3 #include<iostream>
4 #include<algorithm>
5 using namespace std;
6 const int maxn=100005;
7 int v[maxn],dp[maxn],w[maxn];
8 int main()
9 {
10 int n=1,maxx=0;
11 memset(v,0,sizeof(v));
12 while(~scanf("%d",&v[n])) ++n;
13 n--;
14 for(int i=n; i>=1; --i)
15 {
16 dp[i]=1; //记得初始化
17 for(int j=n; j>i; --j) //以i分界
18 {
19 if(v[j]<=v[i] && dp[j]+1>dp[i])
20 dp[i]=dp[j]+1;
21 }
22 maxx=max(maxx,dp[i]); //不要忘了取最大值
23 }
24 int g=0;
25 w[++g]=v[1];
26 for(int i=1;i<=n;++i)
27 {
28 if(w[g]<v[i]) //题目上面说是大于最大高度就不行了,所以等于的情况就不用特别开一个系统
29 {
30 w[++g]=v[i];
31 }
32 else
33 {
34 for(int j=1;j<=g;++j)
35 {
36 if(w[j]>=v[i])
37 {
38 w[j]=v[i];
39 break;
40 }
41 }
42 }
43 }
44 printf("%d\n%d\n",maxx,g);
45 return 0;
46 }

证明第二问的方法:

参考:https://jjpjj.blog.luogu.org/dp-dao-tan-lan-jie

对于问二求整个数列的最长上升子序列即可。证明如下:

(1)假设打导弹的方法是这样的:取任意一个导弹,从这个导弹开始将能打的导弹全部打完。而这些导弹全部记为为同一组,再在没打下来的导弹中任选一个重复上述步骤,直到打完所有导弹。

(2)假设我们得到了最小划分的K组导弹,从第a(1<=a<=K)组导弹中任取一个导弹,必定可以从a+1组中找到一个导弹的高度比这个导弹高(因为假如找不到,那么它就是比a+1组中任意一个导更高,在打第a组时应该会把a+1组所有导弹一起打下而不是另归为第a+1组),同样从a+1组到a+2组也是如此。那么就可以从前往后在每一组导弹中找一个更高的连起来,连成一条上升子序列,其长度即为K;

(3)设最长上升子序列长度为P,则有K<=P;又因为最长上升子序列中任意两个不在同一组内(否则不满足单调不升),则有

P>=K,所以K=P。

第二种方法(nlogn)

二分有一种nlogn的写法,和上面的第二问解法一样,但是要注意这种解法解出来的答案是正确的,但是它过程中的dp序列可能不是我们想要的答案

例如:

5 9 4 1 3 7 6 7

那么:

5 //加入
5 9 //加入
4 9 //用4代替了5
1 9 //用1代替4
1 3 //用3代替9
1 3 7 //加入
1 3 6 //用6代替7
1 3 6 7 //加入

最后b中元素的个数就是最长递增子序列的大小,即4。

要注意的是最后数组里的元素并不就一定是所求的序列,

例如如果输入 2 5 1

那么最后得到的数组应该是 1 5

而实际上要求的序列是 2 5

那么第二问和上一种方法一样,就是用了二分

要注意如果是自己写的二分那没事,如果你用的系统内部函数要注意

默认是支持递增序列

lower_bound:取大于等于的值

upper_bound:取大于的值

但是你可以在他后面加一个自己定义的比较方法,来决定lower_bound和upper_bound所取的值

例:

bool cmp(const int& a,const int& b){return a > b;}

lower_bound(a + 1, a + 1 + n, x, cmp);

或:

lower_bound(a + 1, a + 1 + n, x, greater <int> () );  这里的greater<int>()就是c++友情提供的方便的大于函数

注意得到的值指针,减去原数组就是下标

感觉没什么了,上代码:

 1 #include<stdio.h>
2 #include<string.h>
3 #include<iostream>
4 #include<algorithm>
5 using namespace std;
6 const int maxn=100005;
7 int v[maxn],dp1[maxn],dp2[maxn];
8 int main()
9 {
10 int n=0;
11 while(~scanf("%d",&v[++n]));
12 n--;
13 int q=0,w=0;
14 dp1[++q]=dp2[++w]=v[1];
15 for(int i=2;i<=n;++i)
16 {
17 if(v[i]<=dp1[q]) //这里你也可以把序列反过来用LIS操作 注意等于号
18 {
19 dp1[++q]=v[i];
20 }
21 else
22 {
23 int temp=upper_bound(dp1+1,dp1+1+q,v[i],greater<int>())-dp1; //这里得到的dp[temp]<v[i],不会等于,和原来的意义刚好相反
24 printf("%d %d\n",dp1[temp],v[i]);
25 dp1[temp]=v[i];
26 }
27 if(v[i]>dp2[w]) //就是求原序列最长上升子序列
28 {
29 dp2[++w]=v[i];
30 }
31 else
32 {
33 int temp=lower_bound(dp2+1,dp2+1+w,v[i])-dp2;
34 dp2[temp]=v[i];
35 }
36 }
37 printf("%d\n%d\n",q,w);
38 return 0;
39 }

总结一下:感觉全部都是LIS,就是nlogn那一种方法

如果有错,本菜鸡求dalao指出

P1020 导弹拦截(LIS)的更多相关文章

  1. 洛谷P1020导弹拦截——LIS

    题目:https://www.luogu.org/problemnew/show/P1020 主要是第二问,使用了dilworth定理:一个序列中最长不上升子序列的最大覆盖=最长上升子序列长度. di ...

  2. codevs1044 拦截导弹==洛谷 P1020 导弹拦截

    P1020 导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天 ...

  3. p1020导弹拦截

    传送门 P1020导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度 ...

  4. luogu P1020 导弹拦截 x

    首先上题目~ luogu P1020 导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都 ...

  5. 【题解】P1020 导弹拦截

    [题解]P1020 导弹拦截 从n^2到nlogn 第二问就是贪心,不多说 第一问: 简化题意:求最长不下降子序列 普通n^2: for (int i = 1; i <= n; i++) for ...

  6. 洛谷 P1020 导弹拦截(dp+最长上升子序列变形)

    传送门:Problem 1020 https://www.cnblogs.com/violet-acmer/p/9852294.html 讲解此题前,先谈谈何为最长上升子序列,以及求法: 一.相关概念 ...

  7. 洛谷 P1020导弹拦截题解

    洛谷链接:https://www.luogu.org/problem/P1020 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到 ...

  8. TYVJ P1020 导弹拦截 Label:水

    题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...

  9. P1020 导弹拦截 (贪心+最长不降子序列)

    题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...

随机推荐

  1. springboot异常处理之404

    ps: 推荐一下本人的通用后台管理项目crowd-admin 以及newbee-mall增强版,喜欢的话给个star就好 源码分析 在springboot中默认有一个异常处理器接口ErrorConto ...

  2. 【Java基础】面向对象上

    面向对象上 这一章主要涉及 Java 类及类的成员,包括属性.方法.构造器:代码块.内部类. 面向过程与面向对象 面向过程(Procedure Oriented Programming,POP)与面向 ...

  3. Ossec 安装并配置邮件通知

    Ossec 安装并配置邮件通知 目录 Ossec 安装并配置邮件通知 1. 介绍 2. 软硬件环境 3. 安装步骤 3.1 Server 3.2 Agent 3.3 配置邮件通知 4. 参考资料 1. ...

  4. 【JS学习】数组过滤方法的使用filter

    前言:本博客系列为学习后盾人js教程过程中的记录与产出,如果对你有帮助,欢迎关注,点赞,分享.不足之处也欢迎指正,作者会积极思考与改正. 使用效果: 可以返回参数函数为真的值 //情景:实现从stu数 ...

  5. C#使用OracleParameter操作数据库

    public static int GetScalar(string sql,params OracleParameter [] OracleParms) { using (OracleConnect ...

  6. +load和+initialize方法调用时机

    一.+load方法什么时候调用 +load方法会在runtime加载类.分类时调用(程序运行起来会先去加载调用+load 跟你引用没有引用其头文件没有关系).每个类.分类的+load,在程序运行过程中 ...

  7. 本地jar添加到本地仓库 本地jar依赖无效问题

    最近工作发生了一个很奇怪的事情,我在本地写了一个项目,打包成jar,然后敲命令mvn install:install-file -DgroupId=com.yzwine -DartifactId=yz ...

  8. SAP 摘录数据集

    要在报表中创建并填充摘录数据集,需要执行三步骤:1.将要在摘录数据集中使用的记录类型定义为字段组FIELD-GROUPS该语句定义了字段组,字段组可以将几个字段组合到一个名称下,字段组不为字段保留存储 ...

  9. 全网最详细的PyCharm+Anaconda的安装。

    目录 PyCharm的安装 一.下载安装包 1.安装网站 2.在导航栏输入网址回车 3.点击 DOWNLOAD. 4.它有专业版和社区版,我们下载社区版就可以使用了.(专业版要收费) 二.安装过程 5 ...

  10. Django-http协议

    Http协议:超文本传输协议(应用层程序).它是客户端和服务端请求和应答的标准.Http的请求响应模型:1.客户端连接到web服务器一个http客户端,与web服务器的http端口(默认是80)建立了 ...