洛谷 P1678 烦恼的高考志愿 (二分查找)

在这里插入图片描述

   这个题的思路不止是二分,还有别的方法。别的方法在这里只是提一下思路。第一种: 将学校录取分数线个学生估分放在一个数组里,进行排序,然后找到学生估分,找到离学生估分最近的学校分数线,看两个学校谁与学生估分差值最小。前提条件得对学生估分和学校分数线分别弄个标记,这样才能判断谁是学校分数线,谁是学生估分。 第二种: 使用快慢指针,一个指针a指向学校分数线,一个指针b指向学生估分,如果a指向的分数小于b指向的分数并且(a+1)指向的分数大于b指向的分数,这时候就找到区间,判断哪个小就行。如果a指向的分数小,(a+1)指向的分数也小,那么a++;当找到对应区间并且计算完成后,b++。
   接下来就是二分查找的过程: 先对学校分数线进行排序,然后就遍历学生估分,找到学生估分的对应区间。

 		for(int i=1;i<=n;i++) {
    
    
            int left=1,right=m;
            while(left<(right-1)) {
    
    //这样能保证学生成绩在两个学校之间,如果是left<right,得到的就会是left==right
                int mid=(left+right)/2;
                if(stu[i]>=school[mid]) {
    
    
                    left=mid;
                }
                else right=mid;
            }//找到学生估值所在哪两个学校的录取分数线之间
            x=Math.min(Math.abs(stu[i]-school[left]),Math.abs(school[right]-stu[i]));
            ans+=x;
        }

      这里要说明几个和普通二分查找不同的点:
   1.循环结束的条件是left<(right-1),这样能保证最后的left和right指向的不是一个元素,因为这样循环结束的条件是left==right-1,如果是正常的二分查找,这里是left <=right,这样跳出循环的条件就是left>right,一旦到了这里,就说明没有找到。
   2.如果发现查找的值比中间的值大,left=mid,因为这里是要确定一个区间,如果mid 刚好是区间的左边,那么正确的区间就是mid到right,如果和普通的二分查找一样是left=mid+1,这样就会改变区间的范围。
   上边的while循环跳出来以后,就是确定出来学生成绩在学校录取分数线的范围了,left表示左边界,right表示右边界,然后一样判断left和right谁跟学生估分的差值最小。

import java.util.*;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner sc=new Scanner(System.in);
        int m=sc.nextInt();
        int n=sc.nextInt();
        int[] school=new int[m+1];
        int[] stu=new int[n+1];
        for(int i=1;i<=m;i++)
            school[i]=sc.nextInt();
        for(int j=1;j<=n;j++)
            stu[j]=sc.nextInt();

        Arrays.sort(school,1,m+1);

        int ans=0,x=0;
        for(int i=1;i<=n;i++) {
    
    
            int left=1,right=m;
            while(left<(right-1)) {
    
    //这样能保证学生成绩在两个学校之间,如果是left<right,得到的就会是left==right
                int mid=(left+right)/2;
                if(stu[i]>=school[mid]) {
    
    
                    left=mid;
                }
                else right=mid;
            }//找到学生估值所在哪两个学校的录取分数线之间
            x=Math.min(Math.abs(stu[i]-school[left]),Math.abs(school[right]-stu[i]));
            ans+=x;
        }

        System.out.print(ans);
    }
}