题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制

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

  1. 6
  2. 2

题解:

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

第一问:

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

  1. 1 for(int i=n; i>=1; --i)
  2. 2 {
  3. 3 dp[i]=1;
  4. 4 for(int j=n; j>i; --j)
  5. 5 {
  6. 6 if(v[j]<=v[i] && dp[j]+1>dp[i])
  7. 7 dp[i]=dp[j]+1;//printf("%d %d %d %d\n",v[j],v[i],j,i);
  8. 8 }
  9. 9 maxx=max(maxx,dp[i]);
  10. 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. 1 #include<stdio.h>
  2. 2 #include<string.h>
  3. 3 #include<iostream>
  4. 4 #include<algorithm>
  5. 5 using namespace std;
  6. 6 const int maxn=100005;
  7. 7 int v[maxn],dp[maxn],w[maxn];
  8. 8 int main()
  9. 9 {
  10. 10 int n=1,maxx=0;
  11. 11 memset(v,0,sizeof(v));
  12. 12 while(~scanf("%d",&v[n])) ++n;
  13. 13 n--;
  14. 14 for(int i=n; i>=1; --i)
  15. 15 {
  16. 16 dp[i]=1; //记得初始化
  17. 17 for(int j=n; j>i; --j) //以i分界
  18. 18 {
  19. 19 if(v[j]<=v[i] && dp[j]+1>dp[i])
  20. 20 dp[i]=dp[j]+1;
  21. 21 }
  22. 22 maxx=max(maxx,dp[i]); //不要忘了取最大值
  23. 23 }
  24. 24 int g=0;
  25. 25 w[++g]=v[1];
  26. 26 for(int i=1;i<=n;++i)
  27. 27 {
  28. 28 if(w[g]<v[i]) //题目上面说是大于最大高度就不行了,所以等于的情况就不用特别开一个系统
  29. 29 {
  30. 30 w[++g]=v[i];
  31. 31 }
  32. 32 else
  33. 33 {
  34. 34 for(int j=1;j<=g;++j)
  35. 35 {
  36. 36 if(w[j]>=v[i])
  37. 37 {
  38. 38 w[j]=v[i];
  39. 39 break;
  40. 40 }
  41. 41 }
  42. 42 }
  43. 43 }
  44. 44 printf("%d\n%d\n",maxx,g);
  45. 45 return 0;
  46. 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. 1 #include<stdio.h>
  2. 2 #include<string.h>
  3. 3 #include<iostream>
  4. 4 #include<algorithm>
  5. 5 using namespace std;
  6. 6 const int maxn=100005;
  7. 7 int v[maxn],dp1[maxn],dp2[maxn];
  8. 8 int main()
  9. 9 {
  10. 10 int n=0;
  11. 11 while(~scanf("%d",&v[++n]));
  12. 12 n--;
  13. 13 int q=0,w=0;
  14. 14 dp1[++q]=dp2[++w]=v[1];
  15. 15 for(int i=2;i<=n;++i)
  16. 16 {
  17. 17 if(v[i]<=dp1[q]) //这里你也可以把序列反过来用LIS操作 注意等于号
  18. 18 {
  19. 19 dp1[++q]=v[i];
  20. 20 }
  21. 21 else
  22. 22 {
  23. 23 int temp=upper_bound(dp1+1,dp1+1+q,v[i],greater<int>())-dp1; //这里得到的dp[temp]<v[i],不会等于,和原来的意义刚好相反
  24. 24 printf("%d %d\n",dp1[temp],v[i]);
  25. 25 dp1[temp]=v[i];
  26. 26 }
  27. 27 if(v[i]>dp2[w]) //就是求原序列最长上升子序列
  28. 28 {
  29. 29 dp2[++w]=v[i];
  30. 30 }
  31. 31 else
  32. 32 {
  33. 33 int temp=lower_bound(dp2+1,dp2+1+w,v[i])-dp2;
  34. 34 dp2[temp]=v[i];
  35. 35 }
  36. 36 }
  37. 37 printf("%d\n%d\n",q,w);
  38. 38 return 0;
  39. 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. 剑指offer 面试题0:高质的代码:即考虑边界条件、特殊输入和错误处理

    Q:把一个字符串转换为整数. A1:一个普通但漏洞百出的解法. int StrToInt(char* str) { int number = 0; while (*str != 0) { number ...

  2. SpringMVC文件的上传与下载实现

    单文件上传 首先创建项目,开发工具是IDEA,选择Spring项目,勾选上Spring和SpringMVC. 然后命名,最后完成. 默认生成配置文件在web/WEB-INF下. 首先导入需要的jar包 ...

  3. oracle出现未选定行

    初学oracle,在SQLplus输入查询命令 出现了以下情况.. 后来了解到oracle的SQL语句其中有些词必须大写才会有效. 在这个语句中将username后面的值改为大写就可以了. 还有一种就 ...

  4. 诸葛 VS 庞统,拿下 Paxos 共识算法

    前言 分布式确实是一个有趣的话题,只要你留心观察,分布式在生活中无处不在. 悟空哥最开始学习分布式是从一篇非常用心写的技术征文开始的,而且这篇文章获得了征文第一名,在此感谢掘金社区提供的平台.想学习的 ...

  5. kubernets之secret资源

    一  对于一些保密度比较高的文件,k8s又是如何存储的呢? 针对那些保密度比较高的配置文件,例如证书以及一些认证配置不能直接存储在configmap中,而是需要存储在另外一种资源中,需要对存储在里面的 ...

  6. ctfhub技能树—RCE—综合过滤练习

    打开靶机 查看页面信息 查看源码可以发现这一次过滤了很多东西,查看当前目录信息 查询到%0a为换行符,可以利用这个url编码进行命令注入,开始尝试 http://challenge-2a4584dab ...

  7. CTFshow-萌新赛杂项_劝退警告

    下载附件 https://www.lanzous.com/i9wocah 下载后得到一个劝退警告.zip 解压得到一张gif图片 使用binwalk分析发现包含zip 于是拿到了一个压缩包 打开后发现 ...

  8. SAP GUI用颜色区分不同的系统

    对于经常打开多个窗口的SAP用户,有时候可能同时登录了生产机.测试机和开发机,为了避免误操作,比如在测试要执行的操作,结果在生产机做了,结果可想而知. 虽然可以通过右下角查看再去判断,但是总是没有通过 ...

  9. SAP下载文档为乱码

    通过事物WE60下载的文档为乱码,主要原因是编码格式的不匹配,通常默认的编码格式为ANSI编码,那么我们需要将源码的编码格式转换成UTF-8,这样问题可以解决了.   附:编码格式介绍 不同的国家和地 ...

  10. Docker数据目录迁移解决方案

    场景 在docker的使用中随着下载镜像越来越多,构建镜像.运行容器越来越多, 数据目录必然会逐渐增大:当所有docker镜像.容器对磁盘的使用达到上限时,就需要对数据目录进行迁移. 如何避免: 1. ...