参考
【算法1-6】二分查找与二分答案 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
- P2249 【深基13.例1】查找
- P1102 A-B 数对
- P1873 [COCI 2011/2012 #5] EKO / 砍树
- P1024 [NOIP2001 提高组] 一元三次方程求解
- P1678 烦恼的高考志愿
- P2440 木材加工
- P2678 [NOIP2015 提高组] 跳石头
- P3853 [TJOI2007] 路标设置
- P1182 数列分段 Section II
- P1163 银行贷款
- P3743 kotori的设备
模板
感谢锋哥给我的模板,不用再被边界和while条件以及最后到底取哪个值发愁了;
- 添加一个
ans
变量,l和r初始化为有值的边界下标
,while条件为l<=r
,最后取得的结果就是ans。 - 针对题目问题做判断,对于符合条件的要给ans赋值。
- 例如升序求第一个大于k的那就继续往左找(你想,找到了一个符合这个条件的,但是因为升序,则左边可能还有大于k的),此时
r=mid-1
。
详细代码可见二分查找专题代码.pdf
题目
P2249 【深基13.例1】查找
几个MLE?
package _1_6;
import java.util.Scanner;
public class P2249 {
private static int n;
private static int m;
private static int[] nums;
private static int[] ks;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
m = scanner.nextInt();
nums = new int[n + 1];
ks = new int[m];
for (int i = 1; i <= n; i++) {
nums[i] = scanner.nextInt();
}
for (int i = 0; i < m; i++) {
ks[i] = scanner.nextInt();
}
for (int i = 0; i < m; i++) {
int res = biSearch(ks[i]);
System.out.printf("%d ", res);
}
scanner.close();
}
public static int biSearch(int k) {
int l = 1, r = n;
int ans = -1;
while (l <= r) {
int m = l + (r - l) / 2;
if (nums[m] == k) {
ans = m;
r = m - 1;
} else if (nums[m] < k) {
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
}
修改后:使用快速输入输出(别用Scanner和sout了!!!都给我用StreamTokenizer和PrintWriter)看看这前后差距
Java基础之快速输出输入篇_java快速输入-CSDN博客
java快速输入输出 - Kitorio - 博客园 (cnblogs.com)
package _1_6;
import java.io.*;
public class P2249 {
private static int n;
private static int m;
private static int[] nums;
private static int[] ks;
public static void main(String[] args) throws IOException {
// algo 快速输入:StreamTokenizer
StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
// 注意读之前要nextToken,可以写一个fun
streamTokenizer.nextToken();
n = (int) streamTokenizer.nval;
streamTokenizer.nextToken();
m = (int) streamTokenizer.nval;
nums = new int[n + 1];
ks = new int[m];
for (int i = 1; i <= n; i++) {
streamTokenizer.nextToken();
nums[i] = (int) streamTokenizer.nval;
}
for (int i = 0; i < m; i++) {
streamTokenizer.nextToken();
ks[i] = (int) streamTokenizer.nval;
}
// algo 快速输出:使用sb构造后输出比sout一个个输出占用memory小一点,时间也快
// StringBuilder stringBuilder = new StringBuilder();
// for (int i = 0; i < m; i++) {
// int res = biSearch(ks[i]);
// stringBuilder.append(res).append(" ");
// }
// System.out.println(stringBuilder);
// algo 快速输出:PrintWriter
PrintWriter printWriter = new PrintWriter(System.out);
for (int i = 0; i < m; i++) {
printWriter.printf("%d ", biSearch(ks[i]));
}
// 刷新缓冲区(否则输出不了)
printWriter.flush();
// 关闭输出流
printWriter.close();
}
public static int biSearch(int k) {
int l = 1, r = n;
int ans = -1;
while (l <= r) {
int m = l + (r - l) / 2;
if (nums[m] == k) {
ans = m;
r = m - 1;
} else if (nums[m] < k) {
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
}
P1102 A-B 数对
这道题挣扎了许久,一开始想实现bound,然后狠狠超时
然后用自己的双指针,狠狠WA(目前还不知道为啥,拿不到测试数据)
package _1_6;
import java.io.*;
import java.util.Arrays;
public class P1102 {
private static StreamTokenizer in;
private static PrintWriter out;
private static long[] nums;
private static long c;
private static int n;
public static void main(String[] args) throws IOException {
in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
out = new PrintWriter(System.out);
in.nextToken();
n = (int) in.nval;
in.nextToken();
c = (long) in.nval;
nums = new long[n];
for (int i = 0; i < n; i++) {
in.nextToken();
nums[i] = (long) in.nval;
}
// 先排序
Arrays.sort(nums);
// algo 双指针
long count = 0;
int l = 0, r = 0;
for (int i = 0; i < n; i++) {
// 找到第一个nums[l]-nums[i]>=c,nums[r]-nums[i]>c,则l~r-1区间就是-nums[i]==c
while (l < n && nums[l] - nums[i] < c) l++;
while (r < n && nums[r] - nums[i] <= c) r++;
// 如果l==n则表示找不到对应值
if (l < n && nums[l] - nums[i] == c && r >= 1 && nums[r - 1] - nums[i] == c)
count += r - l;
}
out.println(count);
out.flush();
out.close();
}
// WA /(ㄒoㄒ)/~~
public static void myFun() {
// 先排序
Arrays.sort(nums);
long count = 0;
int startOfA = 0, endOfA = 0, startOfB = 0, endOfB = 0;
while (startOfA < n && startOfB < n) {
// algo 双指针
// 找到A区间
while (endOfA + 1 < n && nums[endOfA + 1] == nums[startOfA]) {
endOfA++;
}
// 找到第一个比 nums[a]+c 大或等的 b
while (startOfB + 1 < n && nums[startOfB] - nums[startOfA] < c) {
startOfB++;
endOfB++;
}
// b已经是最后一个数的话没必要再找了
if (startOfB == n - 1) {
if (nums[startOfB] - nums[startOfA] == c) {
count += endOfA - startOfA + 1;
}
break;
}
// 找到B区间
while (endOfB + 1 < n && nums[endOfB + 1] == nums[startOfB]) {
endOfB++;
}
// nums[startOfB] - nums[startOfA] == c
if (nums[startOfB] - nums[startOfA] == c) {
count += (long) (endOfA - startOfA + 1) * (endOfB - startOfB + 1);
startOfA = endOfA + 1;
endOfA = startOfA;
} else {
// 此时nums[startOfB] - nums[startOfA] > c
startOfA = endOfA + 1;
endOfA = startOfA;
}
}
out.println(count);
}
}
P1873 [COCI 2011/2012 #5] EKO / 砍树
还是要理解题目找到伐木机锯片的最大的整数高度H,使得他能得到的木材至少为M米。
。二分找的是高度,如果得到的木材总数>=m表示至少m米,求最大的H则是找满足这个条件的最大值,所以(总数>=m)符合条件修改ans,且继续往后找(往后就是高度变大,总和变小)
package _1_6;
import java.io.*;
import java.util.Arrays;
public class P1873 {
private static int n;
private static int m;
private static int[] height;
public static void main(String[] args) throws IOException {
StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter printWriter = new PrintWriter(System.out);
streamTokenizer.nextToken();
n = (int) streamTokenizer.nval;
streamTokenizer.nextToken();
m = (int) streamTokenizer.nval;
height = new int[n];
for (int i = 0; i < n; i++) {
streamTokenizer.nextToken();
height[i] = (int) streamTokenizer.nval;
}
Arrays.sort(height);
int ans = -1;
// l r mid 都是高度
int l = 0, r = height[n - 1];
while (l <= r) {
int mid = l + (r - l) / 2;
long sum = count(mid);
// 注意条件,应该是要找符合sum>=m的最大的mid
if (sum >= m) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
printWriter.println(ans);
printWriter.flush();
printWriter.close();
}
private static long count(int h) {
// 从后往前找并计算出砍下来的高度
long res = 0;
for (int i = n - 1; i >= 0; i--) {
if (height[i] <= h) break;
res += height[i] - h;
}
return res;
}
}
P1024 [NOIP2001 提高组] 一元三次方程求解
数学:盛金公式、卡尔丹公式、牛顿迭代法
我都不用(🤭),直接暴力或者二分
二分50%没用题解那个,两个WA没想到为啥
package _1_6;
import java.io.*;
import java.util.ArrayList;
public class P1024 {
private static double a;
private static double b;
private static double c;
private static double d;
private static StreamTokenizer streamTokenizer;
private static PrintWriter printWriter;
private static ArrayList<Double> list;
public static void main(String[] args) throws IOException {
streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
printWriter = new PrintWriter(System.out);
streamTokenizer.nextToken();
a = streamTokenizer.nval;
streamTokenizer.nextToken();
b = streamTokenizer.nval;
streamTokenizer.nextToken();
c = streamTokenizer.nval;
streamTokenizer.nextToken();
d = streamTokenizer.nval;
list = new ArrayList<>();
for (double i = -100; i <= 99; i++) {
// 由根与根之差的绝对值>=1得,每个1开区间内都有可能有一个根,通过<0来判断是否有根,有则二分找
double res1 = fun(i);
double res2 = fun(i + 1);
// 这里可能res2==0计入i+1导致下一轮i重复计入,加个查重
if (!list.contains(i) && res1 == 0) {
printWriter.printf("%.2f ", i);
list.add(i);
}
if (!list.contains(i + 1) && res2 == 0) {
printWriter.printf("%.2f ", i + 1);
list.add(i + 1);
}
if (fun(i) * fun(i + 1) < 0) biSearch(i, i + 1);
}
printWriter.flush();
printWriter.close();
}
public static void biSearch(double l, double r) {
// 如果精度过细没必要继续找了
if (r - l <= 0.001) {
if (!list.contains(l) && fun(l) == 0) {
printWriter.printf("%.2f ", l);
list.add(l);
}
return;
}
double mid = l + (r - l) / 2;
// res判断左右两个区间内是否还有解(解在那个区间)
double res1 = fun(l) * fun(mid);
double res2 = fun(mid) * fun(r);
double res = fun(mid);
if (!list.contains(mid) && res == 0) {
printWriter.printf("%.2f ", mid);
list.add(mid);
return;
}
if (res1 < 0) biSearch(l, mid);
else if (res2 < 0) biSearch(mid, r);
}
public static double fun(double t) {
return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
}
}
这里double高精度有个注意的点,就是结果和指定值只要差距小于一定值即可认为相等,而不是完全等于
package _1_6;
import java.io.*;
import java.util.ArrayList;
public class P1024 {
private static double a;
private static double b;
private static double c;
private static double d;
private static StreamTokenizer streamTokenizer;
private static PrintWriter printWriter;
public static void main(String[] args) throws IOException {
streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
printWriter = new PrintWriter(System.out);
streamTokenizer.nextToken();
a = streamTokenizer.nval;
streamTokenizer.nextToken();
b = streamTokenizer.nval;
streamTokenizer.nextToken();
c = streamTokenizer.nval;
streamTokenizer.nextToken();
d = streamTokenizer.nval;
// biFun();
// 暴力
for (double i = -100; i <= 100; i += 0.01) {
// algo 高精度:对于高精度得计算结果,和结果得差小于一定范围内即可
if (Math.abs(fun(i)) < 0.01) printWriter.printf("%.2f ", i);
}
printWriter.flush();
printWriter.close();
}
public static double fun(double t) {
return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
}
}
d = streamTokenizer.nval;
// biFun();
// 暴力
for (double i = -100; i <= 100; i += 0.01) {
// algo 高精度:对于高精度得计算结果,和结果得差小于一定范围内即可
if (Math.abs(fun(i)) < 0.01) printWriter.printf("%.2f ", i);
}
printWriter.flush();
printWriter.close();
}
public static double fun(double t) {
return Math.pow(t, 3) * a + Math.pow(t, 2) * b + t * c + d;
}
}