P1020 导弹拦截(LIS)
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000 \le 50000≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
111行,若干个整数(个数≤100000 \le 100000≤100000)
输出格式:
222行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
题解:
这道题之前的数据是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)的更多相关文章
- 洛谷P1020导弹拦截——LIS
题目:https://www.luogu.org/problemnew/show/P1020 主要是第二问,使用了dilworth定理:一个序列中最长不上升子序列的最大覆盖=最长上升子序列长度. di ...
- codevs1044 拦截导弹==洛谷 P1020 导弹拦截
P1020 导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天 ...
- p1020导弹拦截
传送门 P1020导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度 ...
- luogu P1020 导弹拦截 x
首先上题目~ luogu P1020 导弹拦截 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都 ...
- 【题解】P1020 导弹拦截
[题解]P1020 导弹拦截 从n^2到nlogn 第二问就是贪心,不多说 第一问: 简化题意:求最长不下降子序列 普通n^2: for (int i = 1; i <= n; i++) for ...
- 洛谷 P1020 导弹拦截(dp+最长上升子序列变形)
传送门:Problem 1020 https://www.cnblogs.com/violet-acmer/p/9852294.html 讲解此题前,先谈谈何为最长上升子序列,以及求法: 一.相关概念 ...
- 洛谷 P1020导弹拦截题解
洛谷链接:https://www.luogu.org/problem/P1020 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到 ...
- TYVJ P1020 导弹拦截 Label:水
题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...
- P1020 导弹拦截 (贪心+最长不降子序列)
题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...
随机推荐
- Java安全之RMI协议分析
Java安全之RMI协议分析 0x00 前言 在前面其实有讲到过RMI,但是只是简单描述了一下RMI反序列化漏洞的利用.但是RMI底层的实现以及原理等方面并没有去涉及到,以及RMI的各种攻击方式.在其 ...
- pip不是内部或外部命令解决方法
问题 已经配置好Python环境,但是安装依赖时,出现pip不是内部或外部命令. 解决方法 找到pip.exe文件所在的目录,将所在路径配置到环境变量path中. 再次输入pip
- 日常采坑:.NetCore上传大文件
一..NetCore上传大文件 .NetCore3.1 webapi 本地测试上传时,遇到一个坑,大点的文件直接失败,根本不走控制器方法. 二.大文件上传配置 IFormFile方式,vs IIS E ...
- python中IF语句容易犯的错误CASE
python中没有switch case类似的语句,但是下面的IF语句却与之类似,却又不同: A = B = C = D = E = 1 if A == 1: B=2 elif B ==2: C= ...
- Java基础复习3
循环的嵌套 public class demo8 { public static void main(String[] args) { /* 输出######## ...
- Java基础复习2
三目运算符 语法:条件判断?表达式1:表达式2; 如果条件判断成立则获取值1否则获取值2 public class demo1{ public static void main(String[ ...
- ProBuilder快速原型开发技术 ---ProBuilder基础操作
在游戏开发.虚拟现实等三维仿真领域,Unity目前是国内外最为知名的开发引擎.随着版本的不断提升与完善,目前Unity2020等最新版本,又增加了很多令人惊奇的功能. Unity内置的ProBuild ...
- 一文告诉你Java日期时间API到底有多烂
前言 你好,我是A哥(YourBatman). 好看的代码,千篇一律!难看的代码,卧槽卧槽~其实没有什么代码是"史上最烂"的,要有也只有"史上更烂". 日期是商 ...
- EasyExcel基本使用
EasyExcel基本使用 一.应用场景 1.数据导入:减轻录入工作量 2.数据导出:统计信息归档 3.数据传输:异构系统之间数据传输 二.简介 Java领域解析.生成Excel比较有名的框架有Apa ...
- gstack pstack strace
gstack pstack strace 通过进程号 查看 进程的工作目录 Linux神器strace的使用方法及实践 - 知乎 https://zhuanlan.zhihu.com/p/180053 ...