题目-查找
代码实现
#include<iostream>
#include<cstring>
#pragma warning(disable:4996)
int arr[1000006] = { 0 };
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
while (m--)
{
int a;
scanf("%d", &a);
int mid = 0;
int l = 1, r = n;
int ans = -1;
while (l <= r)
{
mid = (l + r) / 2;
if (arr[mid] > a)r = mid - 1;
else if (arr[mid] < a)l = mid + 1;
else
{
ans = mid;
r = mid - 1;
}
}
printf("%d ", ans);
}
return 0;
}
难点:
题目本身是一个比较简单的二分思想,给定一个target,寻找这个target的位置。与常规二分法所不同的是,它加入了多个target,这很好实现,简单地循环执行二分查找即可。重点在于,这段数据并不是不重复的,题目又要求我们需要找到这些不重复的数据中最前面的那个数据,这里是额外增加了的一个二分条件应用。
即:当我们找到了一个target,右半边的部分就可以抛弃,从这个位置的左半边继续查找是否有target。(常规中,我们是arr[mid]<target,抛弃右半边)
当然,我们更容易直观想到的是利用while或者for循环,从当前查找到的ans开始,递减地寻找在它之前的重复数据,直到找到一个不重复的数据,输出上一个数据的位置。可惜,这样做会导致超时。
而使用二分查找,只需要做到在找到一个ans后,重复二分查找,这样,如果继续找到了别的ans,这个已经找到的answer会被后找到的、位于它之前的ans刷新,借此来找出最前面的位置下标。
题目-进击的奶牛
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
bool check(int mid, int* arr,int n,int m)
{
int num = 1; int last = 0;
for (int i = 1; i < n; i++)
{
if (arr[i] - arr[last] >= mid)
{
num++;
last = i;
}
}
if (num >= m)
return 1;
else return 0;
}
int main()
{
int n, m;
cin >> n >> m;
int arr[n]={0};
for (int i = 0; i < n; i++)cin >> arr[i];
//序列不为单调序列,所以我们需要排一次序
sort(arr, arr + n);
//记录两头牛可能的最远距离,检查是否满足条件
int R = arr[n - 1] - arr[0],L=0,mid;
int ans = 1;
while (L <= R)
{
mid = (L + R) / 2;
//如果满足条件,那么表明这个mid是符合题意的一个mid,
//于是更新左区间,观察是否有更大的mid使之成立
if (check(mid, arr, n, m))
{
ans = mid;
L = mid + 1;
}
//不满足条件,表明这个mid已经过大了,
//于是更新右区间,查找更小的mid
else
{
R = mid - 1;
}
}
printf("%d", ans);
return 0;
}
题目-跳石头
代码实现
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[50010], n, m, L,ans;
bool check(int x)
{
int last = 0,cnt = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] - last < x)
cnt++;
else
last = a[i];
}
if (cnt > m)
return false;
return true;
}
int main()
{
scanf("%d%d%d", &L, &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
a[++n] = L;
int l = 0, r = L;
while (l <= r)
{
int mid = (l + r) / 2;
if (check(mid))
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
printf("%d\n", ans);
return 0;
}
难点
对于明显要二分答案来做的题,需要书写的二分check条件,在本题中,当距离低于mid时,由于要保证是最大mid,所以小于mid的距离都应该被抛弃,在本题中也就是num++(即移走这块石头),同时,需要注意:虽然题给条件是不允许移走最后一块石头,但是,如果最后一块与倒数第二块之间的距离小于mid,我们也是不可以这样跳过去的,所以实际操作中,最后一块石头也仍然是我们检验的条件。