2020寒假集训专题一搜索I题HYSBZ - 1734 题解(左闭右闭)
原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1734
专题链接:https://vjudge.net/contest/347799#problem/I
思路
开始选择左闭右闭做法是因为博主对二分的原始理解来源于找数字的游戏(就是出题者在【1,100】区间内选一个数字作为正确答案,其他人随意选一个区间内的数字,出题者会告诉大家答案比这个数字大还是小或是正确答案),在这个游戏中,每次都选区间中间的那个数,显然可以最快找到正确答案。
举个例子:如果出题人的正确答案是26,而我选择50,出题人回答“小”,我便知道正确答案区间为【1,49】;如果出题人的答案是61,回答”大 “的话,那么区间就会变成【51,100】.
但稍微想想,不难发现,本题要想用二分查找来做的话,对每一个中间值(mid)的判断只有两种结果——是or否(即能够以中间值为距离建成or不行),与我们做游戏时的三种情况是不同的(小于or大于or等于)。
因此,完全按照原版游戏的思路写代码是肯定不行的。
那么,假如我们将游戏规则改一改,让它与题意契合呢?
我们先将所有可能要用到二分搜索的题意大致分为三种情况
第一种,游戏情况,寻找一个满足题意的"正确答案",也就是寻找这个’‘正确答案’'的位置,这种是最简单的,不多赘述。
第二种,本篇题解的情况,寻找一个满足题意的最大值。
第三种,第二种的反向,寻找一个满足题意的最小值。
根据本题题意,我们把游戏规则改成,出题者只会告诉你是否符合题意。这样,作为一个精明的玩家,你在心中默默更新区间时,同时只要记住那个符合题意的,最大(因为二分的趋向性,也就是最后一个符合题意的数)的数即可。
对应的二分部分代码如下
while (R >=L)//注意是“>=”,这样才能在R==L时再次更新我们的答案,不用担心死循环问题,L==R后的我们的L和R会错开(L>R),让循环自动结束
{
mid = (R + L) / 2;
if (pan(mid))
{
L = mid + 1;
ans = mid;
}
else
R = mid-1;
}
AC代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int N, C;
int cell[100001];
int ans;
bool pan(int d)//这个函数用于判断mid是否合题意
{
int co = 1;
int last = cell[1];
for (int i = 2; i <= N; i++)
{
if (cell[i] - last >= d)
{
last = cell[i];
co++;
if (co == C)
return 1;
}
}
return 0;
}
int main()
{
cin >> N >> C;
for (int i = 1; i <= N; i++)//注意数据是由1开始记录的
scanf("%d", &cell[i]);
sort(cell + 1, cell + N + 1);
int L=1;
int R=cell[N];
int mid=0;
while (R >=L)
{
mid = (R + L) / 2;
if (pan(mid))
{
L = mid + 1;
ans = mid;
}
else
R = mid-1;
}
printf("%d\n",ans);
return 0;
}
另外附上第三种情况下二的二分代码
while (R >=L)
{
mid = (R + L) / 2;
if (pan(mid))
{
R=mid-1;
ans = mid;
}
else
L=mid+1;
}