【JAVA】PAT 1060 爱丁顿数 二分查找JAVA版
题目链接
英国天文学家爱丁顿很喜欢骑车。据说他为了炫耀自己的骑车功力,还定义了一个“爱丁顿数” E ,即满足有 E 天骑车超过 E 英里的最大整数 E。据说爱丁顿自己的 E 等于87。
现给定某人 N 天的骑车距离,请你算出对应的爱丁顿数 E(≤N)。
输入格式:
输入第一行给出一个正整数 N (≤105 ),即连续骑车的天数;第二行给出 N 个非负整数,代表每天的骑车距离。
输出格式:
在一行中给出 N 天的爱丁顿数。
输入样例:
10
6 7 6 9 3 10 8 2 7 8
输出样例:
6
这题看明白题意后,其实思路很简单,把输入按非递增顺序排列
JAVA让数组按非递增方式排序的方法:
Comparator<Integer> t = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
};
Arrays.sort(a, t);
要注意Arrays.sort的参数是sort(T[] a, Comparator<? super T> c),不能用int[]来排序,要用Integer[]。
然后一个个看第i个元素的值是不是大于i+1(i从0开始),E=i+1,当小于或等于的时候,退出循环。不过直接写的话第三个测试点会超时
测试点3超时代码:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
StreamTokenizer in = new StreamTokenizer(bf);
in.nextToken();
int N = (int) in.nval;
Integer[] a = new Integer[N];
for (int i = 0; i < N; i++) {
in.nextToken();
a[i] = (int) in.nval;
}
Comparator<Integer> t = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
};
Arrays.sort(a, t);
int E = 0;
for (int i = 0; i < a.length; i++) {
if (a[i] > (i + 1)) {
E = i+1;
} else {
break;
}
}
out.print(E);
out.flush();
}
}
于是我想是不是一个个遍历太慢了,需要二分法来。边界一直没处理好,老是报错或者Wrong Answer。最后终于AC了
这道题用二分查找的逻辑是要比较a[mid]是不是大于mid+1,同时还要比较mid两边的元素。
- 如果a[mid]>mid+1,还要判断是否a[mid+1]<=mid+2,是的话E=mid+1,否则继续查找。
- 如果a[mid]<=mid+1,还要判断是否a[mid - 1] > mid,是的话E=mid,否则继续查找。
二分查找代码:
static int findE(Integer[] a) {
int E = 0;
int l = 0, r = a.length - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] <= mid + 1) {
if (a[mid - 1] > mid) {
E = mid;
break;
}
r = mid - 1;
} else {
if (a[mid + 1] <= mid + 2) {
E = mid + 1;
break;
}
l = mid + 1;
}
}
return E;
}
由于要比较a[mid]两边的元素,所以还要做一些处理,以防数组越界
- N=1的时候直接判断a[0]是不是大于1
- N=2的时候遍历数组
- N>2的时候再二分查找,同时如果a[0],也就是最大值小于等于1的话直接E=0,不然也会越界
if (N == 1) {
if (a[0] > 1)
E = 1;
} else if (N == 2) {
for (int i = 0; i < a.length; i++) {
if (a[i] <= i + 1) {
E = i;
break;
}
E = i + 1;
}
} else {
if (a[0] <= 1) {
E = 0;
} else {
E = findE(a);//二分查找
}
}
AC代码(二分查找):
import java.util.Arrays;
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
StreamTokenizer in = new StreamTokenizer(bf);
in.nextToken();
int N = (int) in.nval;
int E = 0;
int min = Integer.MAX_VALUE;
Integer[] a = new Integer[N];
for (int i = 0; i < N; i++) {
in.nextToken();
int tem = (int) in.nval;
a[i] = tem;
min = min < tem ? min : tem;
}
if (min > N) {
E = N;
} else {
Arrays.sort(a, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
if (N == 1) {
if (a[0] > 1)
E = 1;
} else if (N == 2) {
for (int i = 0; i < a.length; i++) {
if (a[i] <= i + 1) {
E = i;
break;
}
E = i + 1;
}
} else {
if (a[0] <= 1) {
E = 0;
} else {
E = findE(a);
}
}
}
out.print(E);
out.flush();
}
static int findE(Integer[] a) {
int E = 0;
int l = 0, r = a.length - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] <= mid + 1) {
if (a[mid - 1] > mid) {
E = mid;
break;
}
r = mid - 1;
} else {
if (a[mid + 1] <= mid + 2) {
E = mid + 1;
break;
}
l = mid + 1;
}
}
return E;
}
}
后来去网上看了下别人写的代码和测试点分析,测试点3是所有元素都大于N,这个时候直接输出N就好了,不用一个个遍历。于是我在之前不是二分查找的代码试了一下
判断所有元素是否大于N,一开始我是写在排完序后面
Arrays.sort(a, t);
if (a[a.length-1] > N) {
E = N;
} else {
......//遍历数组
}
但还是超时了,想了想,在输入的时候就把最小的值记录下来,然后试了试
for (int i = 0; i < N; i++) {
in.nextToken();
a[i] = (int) in.nval;
min = min < a[i] ? min : a[i];
}
if (min > N) {
E = N;
} else {
......//排序,遍历数组
}
out.print(E);
out.flush();
然后通过了…
还是我太菜了…
遍历数组完整AC代码:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
StreamTokenizer in = new StreamTokenizer(bf);
in.nextToken();
int N = (int) in.nval;
int E = 0;
int min = Integer.MAX_VALUE;
Integer[] a = new Integer[N];
for (int i = 0; i < N; i++) {
in.nextToken();
a[i] = (int) in.nval;
min = min < a[i] ? min : a[i];
}
if (min > N) {
E = N;
} else {
Comparator<Integer> t = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
};
Arrays.sort(a, t);
for (int i = 0; i < a.length; i++) {
if (a[i] > (i + 1)) {
E = i + 1;
} else {
break;
}
}
}
out.print(E);
out.flush();
}
}