二分查找 (模板+例题)

板子

int BinarySearch(int a[], int size, int num) {//参数分别为:数组,数组元素的个数,待查找的元素 
  int left = 1; right = size, mid;
  while(left <= right) {
    mid = left + (right-left) / 2;
    if(num==a[mid]) return mid;//找到了这个数,返回它的下标 
    else if(num > a[mid]) left = mid + 1;
    else right = mid - 1;
  }
  return -1;//找不到,返回-1 
} 

PS:这里提一下取区间中点的操作,int mid = (left+right)/2;容易发生整形溢出,所以用int mid = left + (right-left) / 2;

例题1保龄球
题目描述

DL 算缘分算得很烦闷,所以常常到体育馆去打保龄球解闷。因为他保龄球已经打了几十年了,所以技术上不成问题,于是他就想玩点新花招。

DL 的视力真的很不错,竟然能够数清楚在他前方十米左右每个位置的瓶子的数量。他突然发现这是一个炫耀自己好视力的借口——他看清远方瓶子的个数后从某个位置发球,这样就能打倒一定数量的瓶子。

1 OOO

2 OOOO

3 O

4 OO

如上图,每个“O”代表一个瓶子。如果 DL 想要打倒 3 个瓶子就在 1 位置发球,想要打倒 4 个瓶子就在 2 位置发球。

现在他想要打倒 m 个瓶子。他告诉你每个位置的瓶子数,请你给他一个发球位置。
输入格式

输入文件名为 bowling.in。

第一行包含一个正整数 n,表示位置数。

第二行包含 n 个正整数,第 i 个数。表示第 i 个位置的瓶子数,保证各个位置的瓶子数不同。

第三行包含一个正整数 Q,表示 DL 发球的次数。

第四行至文件末尾,每行包含一个正整数 m,表示 DL 需要打倒 m 个瓶子。
输出格式

输出文件名为 bowling.out。

共 Q 行。每行包含一个整数,第 i 行的整数表示 DL 第 i 次的发球位置。若无解,则输出 0。
输入输出样例
输入 #1

5
1 2 4 3 5
2
4
7

输出 #1

3
0

说明/提示

【数据范围】

对于 50%的数据,1 ≤ n,Q ≤ 1000,1 ≤ai,M ≤ 10^5

对于 100%的数据,1 ≤ n,Q ≤ 100000,1 ≤ai,M ≤ 10^9

模板题,就不多解释了,直接上代码

#include<cstdio>
#include<algorithm>
const int N = 1e5 + 50;
using namespace std;
struct node{
    int data;
    int index;
}a[N];
bool cmp(node x ,node y) { 
     return x.data < y.data;
}
int main() {
    int n, t, x;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
      scanf("%d", &a[i].data);
      a[i].index = i;
    }
    sort(a+1, a+n+1, cmp);
    scanf("%d",&t);
    for(int i = 1; i <= t; ++i) {
      scanf("%d", &x);
      int left = 1, right = n,  mid;
      while(left <= right) {
        mid = left + (right-left) / 2;
        if(x == a[mid].data) break;
        else if(x > a[mid].data) left = mid + 1;
        else right = mid - 1;
      }
      if(x== a[mid].data) {
        printf("%d\n",a[mid].index);
      }else puts("0");
    }
    return 0;
}

例题2高考志愿
题目背景

计算机竞赛小组的神牛V神终于结束了万恶的高考,然而作为班长的他还不能闲下来,班主任老t给了他一个艰巨的任务:帮同学找出最合理的大学填报方案。可是v神太忙了,身后还有一群小姑娘等着和他约会,于是他想到了同为计算机竞赛小组的你,请你帮他完成这个艰巨的任务。
题目描述

现有 m(m≤100000) 所学校,每所学校预计分数线是 ai(ai≤1000000)。有 n(n≤100000)位学生,估分分别为 bi(bi≤1000000)。

根据n位学生的估分情况,分别给每位学生推荐一所学校,要求学校的预计分数线和学生的估分相差最小(可高可低,毕竟是估分嘛),这个最小值为不满意度。求所有学生不满意度和的最小值。
输入格式

第一行读入两个整数m,n。m表示学校数,n表示学生数。第二行共有m个数,表示m个学校的预计录取分数。第三行有n个数,表示n个学生的估分成绩。
输出格式

一行,为最小的不满度之和。
输入输出样例
输入 #1

4 3
513 598 567 689
500 600 550

输出 #1

32

说明/提示

数据范围:

对于30%的数据,m,n<=1000,估分和录取线<=10000;

对于100%的数据,n,m<=100,000,录取线<=1000000。
PS:为了更简洁的写出代码,这里介绍一下c++自带的查找函数lower_bound( )和upper_bound( ),附上我 转载 的博客链接 双击进入

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
template<class T>
void read(T &res) {//数据有点多,加了个快读
  int f = 1 ;res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar();}
}
int main() {
    int m, n, t;
    long long sum = 0;
    read(m); read(n);
    int a[m];
    for(int i = 0; i < m; ++i) {
      read(a[i]);
    }
    sort(a, a + m);
    for(int i = 0; i < n; ++i) {
      read(t);
      int x = lower_bound(a, a + m, t) - a;
      if(x==0) {//如果全部分数线都大于等于该学生分数
        sum += (a[0] - t);
      }
      else 
        if(x==m) {//如果全部分数线都小于等于该学生分数。
          sum += (t - a[m-1]);
        } else {
        sum += min(abs(a[x]-t), abs(t-a[x-1]));
      }
    }
    cout<<sum<<"\n";
    return 0;
}

分析:需要一点思路,排序之后,找到录取分数线中第一个大于等于该学生分数的分数,取这个分数与学生分数的绝对值,取这个分数前一个分数与学生分数的绝对值,取这两个绝对值中较小值就是该学生的不满意度。然后把所有学生的不满意度加起来就是答案。
PS:班长挺辛(shu)苦(fu)的,能配合工作就别对着干,这(gan)样(de)不(piao)好(liang)
例题3A-B问题
题目描述

出题是一件痛苦的事情!

题目看多了也有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

好吧,题目是这样的:给出一串数以及一个数字 C,要求计算出所有 A-B=C 的数对的个数。(不同位置的数字一样的数对算不同的数对)
输入格式

第一行包括2个非负整数N和C,中间用空格隔开。

第二行有N个整数,中间用空格隔开,作为要求处理的那串数。
输出格式

输出一行,表示该串数中包含的所有满足A−B=C的数对的个数。
输入输出样例
输入 #1

4 1
1 1 2 3

输出 #1

3

说明/提示

对于73%的数据,N≤2000;

对于100%的数据,N≤200000。

所有输入数据都在longint范围内。

2017/4/29新添数据两组

#include<iostream>
#include<algorithm>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    long  n, c, sum = 0;
    cin>>n>>c;
    long a[n+1];
    for(int i = 0; i < n; ++i) {
      cin>>a[i];
    }
    sort(a , a + n);
    for(int i = 0; i < n; ++i) {
      sum += (upper_bound(a, a+n, a[i]+c)-a) - (lower_bound(a, a+n, a[i]+c)-a);
    }
    cout<<sum<<"\n";
    return 0;
}

尽管我写出了这题,但是没有如上代码如此简洁明了。流下了不学无术的眼泪

解释一下原理:如果这个数组是有序的,那么对于每一个A的值,在它的后方就只有一个数值B满足A,B的差值为C。

这时我们只需要使用两个函数求出数组中对于每个A,A+C的这两个位置,它们的差即为数组中数值为A+C的元素个数。将这个数加到sum中,完美输出。

发布了5 篇原创文章 · 获赞 2 · 访问量 436

猜你喜欢

转载自blog.csdn.net/qq_45886817/article/details/104395319