(1)P2249 【深基13.例1】查找
这道题本身并不难,典型的一个二分查找,用java自带的二分查找函数就可以实现.但是这题存在多个值相同的情况,根据例题我们知道,返回的是第一个数字。但如果直接用二分搜索肯定不能保障搜索到的是第一个,那么 我们就要自己手写一个二分查找,并且当找到了元素后再判断一下是不是第一个,如果不是我们就往前移。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter out=new PrintWriter(System.out);
String[] s=br.readLine().split(" ");
int n=Integer.valueOf(s[0]),m=Integer.valueOf(s[1]);
int[] arr=new int[n+1];
s=br.readLine().split(" ");
for(int i=1;i<=n;i++) {
arr[i]=Integer.valueOf(s[i-1]);
}
s=br.readLine().split(" ");
for(int i=0;i<m;i++) {
out.print(binarySearch(1, n, arr, Integer.valueOf(s[i]))+" ");
}
out.close();
}
static int binarySearch(int l,int r,int[] arr,int k) {
int res=-1;
while(l<=r) {
int m=(l+r)/2;
if(arr[m]==k) {
res=m;
break;
}
else if(arr[m]>k) {
r=m-1;
}
else if(arr[m]<k) {
l=m+1;
}
}
if(res!=-1) {
while(arr[res-1]==k&&res!=1) {
res--;
}
}
return res;
}
}
(2)P1102 A-B 数对
这道题有一个非常巧妙的方法,利用Map集合,首先遍历一遍数组,将各个数字和它出现的次数用map存储起来。然后将数组中每个元素减c。最后再遍历一遍数组,把数字中每个元素出现的次数累加起来。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String[] s=br.readLine().split(" ");
int n=Integer.valueOf(s[0]),m=Integer.valueOf(s[1]);
HashMap<Integer, Integer> hs=new HashMap<Integer, Integer>();
int[] arr=new int[n];
s=br.readLine().split(" ");
for(int i=0;i<n;i++) {
arr[i]=Integer.valueOf(s[i]);
if(hs.containsKey(arr[i])) {
hs.put(arr[i], hs.get(arr[i])+1);
}
else {
hs.put(arr[i], 1);
}
arr[i]-=m;
}
long sum=0;
for(int i=0;i<n;i++) {
if(hs.containsKey(arr[i]))
sum+=hs.get(arr[i]);
}
System.out.println(sum);
}
}
(3)P1873 砍树
一道二分答案题目,像这种题目一般都是有一个模板的。用二分来选取一个砍树的高度,然后写个函数判断这个长度是否满足条件,如果满足,则在更高的长度进行选取,不满足则在低的长度选取。
ps:这题有一个点用java做会超内存。。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int[] arr;
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String[] s=br.readLine().split(" ");
int n=Integer.valueOf(s[0]),m=Integer.valueOf(s[1]);
arr=new int[n];
s=br.readLine().split(" ");
for(int i=0;i<n;i++) {
arr[i]=Integer.valueOf(s[i]);
}
int l=1,r=1000000000;
int max=0;
while(l<=r) {
int mid=(l+r)/2;
if(judge(mid,m)) {
max=mid;
l=mid+1;
}
else {
r=mid-1;
}
}
System.out.println(max);
}
public static boolean judge(int mid,int k) {
int sum=0;
for(int i=0;i<arr.length;i++) {
if(arr[i]>mid) {
sum+=arr[i]-mid;
}
if(sum>=k) {
return true;
}
}
return false;
}
}
(4)P1024 一元三次方程求解
首先,解的范围是-100到100,而且每个解绝对值差大于1,那么可以枚举每个区间,每个区间的大小为1。然后把区间端点带入方程,如果左端点为0,直接输出左端点。否则用二分的思想,在区间内寻找解,因为要满足f(x1)*f(x2)<0才可能有解
import java.util.Scanner;
public class Main {
static double a,b,c,d;
public static double f(double x) {
return a*x*x*x+b*x*x+c*x+d;
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
a=sc.nextDouble();b=sc.nextDouble();c=sc.nextDouble();d=sc.nextDouble();
double l,r;
for(int i=-100;i<100;i++) {
l=i;r=i+1;
double f1=f(l);
double f2=f(r);
if(f1==0)
{
System.out.print(String.format("%.2f", l)+" ");
}
if(f1*f2<0) {
while(r-l>=0.001) {
double m=(l+r)/2;
if(f(m)*f(r)<=0)
l=m;
else
r=m;
}
System.out.print(String.format("%.2f", r)+" ");
}
}
}
}
(5)P1678 烦恼的高考志愿
一道二分搜索的题目。首先把学校分数线进行排序,然后遍历学生的分数,对每一个学生分数,二分的去学校分数线找最接近学生分数的分数线。然后对这个分数线左边右边和本身这三个数取一个最小值,这个最小值就是估分的误差。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int m=sc.nextInt(),n=sc.nextInt();
int[] arr=new int[m];
for(int i=0;i<m;i++) {
arr[i]=sc.nextInt();
}
Arrays.sort(arr);
long sum=0;
int ans,l,r,mid=0,res=Integer.MAX_VALUE;
for(int z=0;z<n;z++) {
ans=sc.nextInt();
l=0;r=m-1;
while(l<=r) {
mid=(l+r)/2;
if(ans==arr[mid]) {
break;
}
else if(arr[mid]<ans){
l=mid+1;
}
else {
r=mid-1;
}
}
res=Math.abs(arr[mid]-ans);
if(mid!=0) res=Math.min(res, Math.abs(arr[mid-1]-ans));
if(mid!=m-1) res=Math.min(res,Math.abs(arr[mid+1]-ans));
sum+=res;
}
System.out.println(sum);
}
}
(6)P2440 木材加工
这道题和砍树那道题几乎就是一个题,都是二分出一个答案,然后用一个函数验证这个答案是否可行,可行就在数组的右边区间继续二分答案,不可行就在数组左边二分答案。这里注意下1长度都切不出来,直接特判0.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++) {
arr[i]=sc.nextInt();
}
int l=0,r=1000000000,mid,sum,max=0;
while(l<=r) {
mid=(l+r)/2;
if(mid==0) {
System.out.println(0);
return;
}
sum=0;
for(int i=0;i<n;i++) {
sum+=arr[i]/mid;
}
if(sum>=m) {
max=mid;
l=mid+1;
}
else {
r=mid-1;
}
}
System.out.println(max);
}
}
(7)P2678 跳石头
这道题也是用二分去搜索最长跳跃的距离,但在判断这个距离是否符合要求的时候需要思考一下如何判断:我们先用一个数组把所有的石头包括起点和终点的石头存下来,然后每当我们选定一个跳跃的距离,我们从第一个石头开始,它和它前面的一块石头的距离,是否大于我们跳跃的距离,如果满足,那么我们继续从这块石头跳,下一次的比较就是和这块石头进行比较距离。如果不满足,那么我们只好移除掉脚下这块石头,使得他可以跳更远的石头来满足我们选定的跳跃距离。最后判断我们移除的石头数量是否小于它给定的,来进行下次二分搜索跳跃距离。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt(),k=sc.nextInt();
int[] arr=new int[m+2];
for(int i=1;i<=m;i++) {
arr[i]=sc.nextInt();
}
arr[m+1]=n;
int l=1,r=100000000,mid,ans,last,max=0;
while(l<=r) {
mid=(l+r)/2;
ans=0;last=0;
for(int i=1;i<=m+1;i++) {
if(arr[i]-last<mid) {
ans++;
}
else {
last=arr[i];
}
}
if(ans<=k) {
max=mid;
l=mid+1;
}
else {
r=mid-1;
}
}
System.out.println(max);
}
}
(8)P3853 [TJOI2007]路标设置
和上题其实意思差不多,不过上题是最短的跳跃距离最长,这题是最长的公里距离最短,其实做法都是差不多的。我们二分选定一个长度,如果两个路标直接距离大于这个我们给定的长度,那么就要在中间加设路标,直到路标之间都小于等于我们给定的长度。最后判断我们加设路标的数量是否满足题目。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt(),k=sc.nextInt();
int[] arr=new int[m];
for(int i=0;i<m;i++) {
arr[i]=sc.nextInt();
}
int l=0,r=n,mid,sum,last,max=0;
while(l<=r) {
mid=(l+r)/2;sum=0;last=0;
for(int i=1;i<m;i++) {
while(arr[i]-last>mid) {
last+=mid;
sum++;
}
last=arr[i];
}
if(sum<=k) {
max=mid;
r=mid-1;
}
else {
l=mid+1;
}
}
System.out.println(max);
}
}
(9)P1182 数列分段 Section II
这道题我们二分搜索最大值,每次选定一个最大值,然后判断所有数段进行分段,如果分段的数量小于等于题目给定的分段数,说明这个最大值可以再小,如果分段大于我们分的段,那么这个最大值应该取大些。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class Main {
static int[] arr;
static int n,m;
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String[] s=br.readLine().split(" ");
n=Integer.valueOf(s[0]);
m=Integer.valueOf(s[1]);
arr=new int[n];
s=br.readLine().split(" ");
int l=0,r=0;
for(int i=0;i<n;i++) {
arr[i]=Integer.valueOf(s[i]);
r+=arr[i];
l=Math.max(l, arr[i]);
}
int max=0;
while(l<=r) {
int mid=(l+r)/2;
if(judge(mid)) {
l=mid+1;
}
else {
r=mid-1;
}
}
System.out.println(l);
}
public static boolean judge(int k) {
int total=0,tim=1;
for(int i=0;i<n;i++){
if(total+arr[i]<=k)
total+=arr[i];
else {
total=arr[i];
tim++;
}
}
return tim>m;
}
}
(10)P1163 银行贷款