二分查找的前提是该数组是有序的
1.二分查找求下标
int binary(int a[], int size, int p)
{
int low = 0;
int high = size - 1;//左右端点
int mid;
while (low <= high)
{
mid = low + (high - low) / 2;
if (p == a[mid])
return mid;
else if (p > a[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
2.二分查找求最大最小值
int low(int a[],int size, int p)
{
int low = 0, high = size-1;
int mid;
while (low<=high)
{
mid = low + (high - low) / 2;
if (a[mid] < p)//如果a[mid]<p时说明此时a[mid]小于p成立,但不一定是最优解,可能还有大于mid但比p小的数
low = mid + 1;
else
high = mid - 1;
}
return low - 1;// 因为找到mid之后还会测试mid之后一个数,所以返回的数应该是low - 1;
}
3.二分查找的应用
疯牛
时间限制:1000 ms | 内存限制:65535 KB
难度:4
描述
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入
有多组测试数据,以EOF结束。
第一行:空格分隔的两个整数N和C
第二行——第N+1行:分别指出了xi的位置
输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1
2
8
4
9
样例输出
3
题意:
总共有n个隔间,其中有c头牛有单独占据一个牛圈。问这c头牛间的最大的最小距离。
提示:要求的是最大的最小距离,可以先找出可能距离的范围
#include<iostream>
#include<algorithm>
using namespace std;
int n, c, a[100005];
bool judge(int mid)
{
int num = 1;//假设已经放了一头牛
int pre = a[0];//pre是前一个牛舍的编号,根据贪心思想第一间牛舍应该放一头牛
for (int i = 1; i < n; i++) //依据牛舍进行判断
{
if (a[i] - pre >= mid)//如果a[i]-前一个牛舍编号大于等于mid,则在a[mid]牛舍放入一头牛,num++
{
num++;
pre = a[i];//将前一个放牛的牛舍编号改为a[i];
if (num >= c)return true;//如果可以放入的牛数量大于等于总牛数量,那么证明此mid可行,返回true
}
}
return false;
}
/* 暂时不知道为什么不对
bool judge(int mid)
{
int pre = a[0];
for (int i = 1; i < c; i++)
{
int j = 1;//牛舍
while (a[j] - pre<mid)
{
j++; // 1 2 4 8 9
if (j>n - 1) return false;
}
pre = a[j];
}
*/
int main()
{
while (~scanf("%d %d", &n, &c))
{
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a + n);
int low = 0, high = a[n - 1] - a[0];//确定牛舍之间可能的范围
int mid;
while (low <= high)
{
mid = low + (high - low) / 2;
if (judge(mid))
low = mid + 1;
else
high = mid - 1;
}
cout << low - 1 << endl;
}
return 0;
}
01:派
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。
输入
第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出
输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
3 3
4 3 3
样例输出
25.133
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
double pi = 3.14159265358979323846;
int n, f;
double s[10005];//存储每个派的体积
bool judge(double mid)
{
int num = 0;//记录所能分得所有的派的数量,初始化为零
for (int i = 0; i<n; i++)//记录在体积为mid时,每个派所能分得数量
{
num += int(s[i] / (mid * 1000000));
}
if (num >= f + 1) return true;//如果当体积为mid时所分的数量大于等于总人数(f+1)时就是这种方案可以保证每人都有,返回true
else return false;
}
int main()
{
scanf("%d %d", &n, &f);
for (int i = 0; i<n; i++)
{
double r;
cin >> r;
s[i] = pi*r*r * 1000000;//乘1000000是为了使小数点后移,保证精确度
}
sort(s, s + n);
二分查找,是要求其最小的最大体积,所以要确定提及可能的变化范围
double low = 0, high = s[n - 1], mid;//变化范围为0--s【n-1】
while (high>=low)
{
mid = low + (high - low) / 2;
if (judge(mid)) //如果当mid符合时则继续对low后移,因为此时的mid符合但不一定是最优解
low = mid + 0.00001;
else high = mid - 0.00001;
}
printf("%.3lf", mid);
return 0;
}
02:河中跳房子
描述
每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和离起点L远 (1 ≤ L≤ 1,000,000,000) 的终点处均有一个岩石。在起点和终点之间,有N (0 ≤ N ≤ 50,000) 个岩石,每个岩石与起点的距离分别为Di (0 < Di < L)。
在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一个岩石跳到另一个岩石。当然,实力不济的奶牛是没有办法完成目标的。
农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移走除起点和终点外的至多M (0 ≤ M ≤ N) 个岩石。
请帮助约翰确定移走这些岩石后,最长可能的最短跳跃距离是多少?
输入
第一行包含三个整数L, N, M,相邻两个整数之间用单个空格隔开。
接下来N行,每行一个整数,表示每个岩石与起点的距离。岩石按与起点距离从近到远给出,且不会有两个岩石出现在同一个位置。
输出
一个整数,最长可能的最短跳跃距离。
样例输入
25 5 2
2
11
14
17
21
样例输出
4
提示
在移除位于2和14的两个岩石之后,最短跳跃距离为4(从17到21或从21到25)。
#include<iostream>
#include<algorithm>
using namespace std;
int L, n, m;
int a[50002];
int judge(int mid)
{
int num = 0;//记录所去除的石头数量
int pre = 0;//pre表示前一石头距离岸边的距离
for (int i = 1; i <= n; i++)
{
if (a[i] - pre < mid)
{
num++;
if (num>m) return 0;
}
else
{
pre = a[i];
}
}
return 1;
}
int main()
{
scanf("%d %d %d", &L, &n, &m);
for (int i = 1; i <= n; i++)
cin >> a[i];
a[++n] = L;//还要把终点距离起点的距离
sort(a + 1, a + n + 1);
int low = 0, high = L;//确定石头之间距离可能的范围
int mid;
while (low <= high)
{
mid = low + (high - low) / 2;
if (judge(mid))
low = mid + 1;
else
high = mid - 1;
}
cout << low - 1 << endl;
}
总结:
个人认为,二分法求解最大的最小值时,二分的是题目需要求的范围,例如派的体积、石头之间的距离、牛舍之间的距离……
if (a[mid] < p)//如果a[mid]<p时说明此时a[mid]小于p成立,但不一定是最优解,可能还有大于mid但比p小的数
low = mid + 1;
else
high = mid - 1;
上方代码low=mid+1,是要尝试下一个值是不是最优解,因为if(a[mid]<屁)成立,只能证明mid可以满足条件,但并不代表就是最大的最小值,所以要继续向后逼近(因为数组a是经过排序的);