[洛谷][P1020]导弹拦截(Java)(二分)

博客围绕导弹拦截问题展开,介绍了题目描述、输入输出格式等。解题思路上,第一问求系统最多能拦截的导弹数,是最长下降子序列问题;第二问求拦截所有导弹最少配备的系统数,是最长上升子序列问题。还反思了错误,并给出了Java代码及测试结果。

原题链接:P1020 导弹拦截

题目描述

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

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

输入输出格式

输入格式:

 

1行,若干个整数(个数≤100000)

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

389 207 155 300 299 170 158 65

输出样例#1: 复制

6
2

说明

为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

每点两问,按问给分


解题思路:

第一问还是很好想到的,最长下降子序列。用二分即可求出,不会做的去LeetCode看看吧,暂时我还没写,所以没链接。

主要是第二问,多少套系统?

其实思路 和第一问类似,,不过是求最长上升子序列。

试想一下,每套系统都肯定有一个拦截的最低导弹,那么,我的第一套系统拦截了一颗较低的导弹,假如下一颗导弹要高于这颗,那么就需要新的拦截系统了。

举个例子:【10,9,5,6,8,2,1】

我的第一套系统 equ[0] 先拦截10,在拦截9(这时equ[0]更新为9),再拦截5(equ[0]==5);

到 6 的时候,equ[0]==5  < 6,所以需要一个新的系统去拦截也就是equ[1]==6;

这样一看,实际上就是再求最长上升子序列问题。

如此一来就好做了。 

 

反思错误:

①没看清题,以后的每发“不高于”前一发。。可以等于的。

②因为习惯用快读,结果快读不会无限制的输入。错了一波,尴尬,现在的快读无限制输入也是瞎写的。

 

java代码(代码量看着多,实际上很少,在SO的solution才是代码):O(nlogn)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) {
//		long sta = System.nanoTime();

		InputStream is = System.in;
		OutputStream os = System.out;

		IN cin = new IN(is);
		PrintWriter cout = new PrintWriter(os);

		SO so = new SO();
		so.solution(cin, cout);

//		long end = System.nanoTime();
//		cout.println("耗时:" + (double)(end-sta)/1e6 + "ms");

		cout.close();
	}

	static final int MOD = (int)1e9 + 7;
	static class SO {
		int max = 100000;
		int[] cat = new int[max+1];
		int top1 = 0;
		int[] equ = new int[max+1];
		int top2 = 0;
		
		void one(int cur) {
			int l = 0, r = top1-1;
			while(l<r) {
				int m = l + (r-l)/2;
				if(cat[m]>=cur)l=m+1;
				else r=m;
			}
			cat[r] = cur;
		}
		void two(int cur) {
			int l = 0, r = top2-1;
			while(l<r) {
				int m = l + (r-l)/2;
				if(equ[m]<cur)l=m+1; 
				else r=m;
			}
			equ[r] = cur;
		}
		void solution(IN cin, PrintWriter cout) {
			String s;
			int cur;
			while((s=cin.next())!=null) {
				cur = Integer.parseInt(s);
				if(top1==0 || cat[top1-1]>=cur)cat[top1++]=cur;
				else one(cur);
				if(top2==0 || equ[top2-1]<cur)equ[top2++]=cur;
				else two(cur);
			} 
			cout.println(top1+"\n"+top2);
			
		}//end solution
	}//end SO
	
	//以下是快读部分
	static class IN {
		private BufferedReader reader;
		private StringTokenizer tokenizer;
		private String s;

		IN(InputStream is) {
			reader = new BufferedReader(new InputStreamReader(is), 32768);
			tokenizer = null;
		}
		
		public String next() {
			while (tokenizer == null || !tokenizer.hasMoreTokens()) {
				try {
					s = reader.readLine();
					if(s==null)return null;
					tokenizer = new StringTokenizer(s);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			return tokenizer.nextToken();
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

	}
}

 

测试结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值