NC235558 牛可乐和魔法封印
链接:https://ac.nowcoder.com/acm/problem/235558
来源:牛客网
- 为了对付搜索不到的情况,人为在前后加入了边界(延长数据区间,使得必定有解),可以减少对边界的处理
- 二分循环退出后,l和r其实是相等的,取任意一个做答案即可
下面分别是加和不加边界的版本(注意,两种二分选取的边界不同,注意甄别)
加
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
double s[N];
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> s[i];
s[0] = -0x3f3f3f3f;
s[n + 1] = 0x3f3f3f3f;
int temp;
cin >> temp;
while (temp--)
{
int a, b;
cin >> a >> b;
int l = 0;
int r = n + 1;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (s[mid] < a)
l = mid;
else
r = mid - 1;
}
int ans1 = r;
l = 0;
r = n + 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (s[mid] > b)
r = mid;
else
l = mid + 1;
}
int ans2 = l;
cout << ans2-ans1-1 << endl;
}
}
不加
不加边界需要额外处理:1.区间在两侧且完全没有重合的情况
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
double s[N];
signed main()
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> s[i];
// s[0] = -0x3f3f3f3f;
// s[n + 1] = 0x3f3f3f3f;
int temp;
cin >> temp;
while (temp--)
{
int a, b;
cin >> a >> b;
if(b<s[0]||a>s[n-1])
{
cout<<0<<endl;continue;
}
int l = 0;
int r = n - 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (s[mid] < a)
l = mid + 1;
else
r = mid;
}
int ans1 = l;
l = 0;
r = n - 1;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (s[mid] > b)
r = mid - 1;
else
l = mid;
}
int ans2 = l;
cout << ans2 - ans1 + 1 << endl;//对于+1这种在输出上对答案做出改变的,特别容易
//出边界错误
}
}
[USACO 2009 Dec S]Music Notes
链接:https://ac.nowcoder.com/acm/problem/24866
来源:牛客网
既然题目询问某时刻在的弹奏音符,可以用前缀和来记录每个音符的结束时间,然后二分搜索
大于等于当前询问的数字,索引即为正在弹奏的音符
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
double s[N];
int sum[N];
signed main()
{
cin>>n>>m;
sum[0]=-1;
for(int i=1;i<=n;i++)
{cin>>s[i];sum[i]=sum[i-1]+s[i];}
while(m--)
{
int now;cin>>now;
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(sum[mid]<now)l=mid+1;
else r=mid;
}
cout<<l<<endl;
}
}
[USACO 2016 Jan S]Angry Cows
链接:https://ac.nowcoder.com/acm/problem/24017
来源:牛客网
二分答案,由check函数去判断此答案是否满足条件,知道找到最小值,同时check里面也可以运用二分判断是否能满足条件
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
int s[N];
int sum[N];
int check(int mid)
{
// cout << mid << endl;
int ans = 0;
int cnt = 0;
int now = 0;
for (int i = 0; i < n;)
{
int now = s[i] + mid;
int l = i;
int r = n;
while (l < r)
{
int middie = (l + r) >> 1;
if (s[middie] <= now)
l = middie + 1;
else
r = middie;
}
ans++;
if (ans > m)
return false;
i = l;
}
return true;
}
signed main()
{
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> s[i];
sort(s, s + n);
int l = 1;
int r = n;
s[n] = 0x3f3f3f3f;//防止搜不到答案的边界处理问题
while (l < r)
{
int mid = (l + r) >> 1;
if (!check(mid * 2))
l = mid + 1; // 不可以满足
else
r = mid;
// cout << mid << endl;
}
cout << l << endl;
}
NC16462 [NOIP2015]跳石头
思路这里就引用洛谷题解上ShawnZhou大佬的思路吧
注意到题面:使得选手们在比赛过程中的最短跳跃距离尽可能长。如果题目规定了有“最大值最小”或者“
最小值最大”的东西,那么这个东西应该就满足二分答案的有界性(显然)和单调性(能看出来)。
使用一个check判断这个解是不是可行解。如果这个解是可行解,那么有可能会有比这更优的解,那么我们就去它的右边二分。为什么去右边?答案是,这个区间是递增的 ,而我们求的是最短跳跃距离的最大值,显然再右边的值肯定比左边大,那么我们就有可能找到比这更优的解,直到找不到,那么最后找到的解就有理由认为是区间内最优解。反过来,如果二分到的这个解是一个非法解,我们就不可能再去右边找了。因为性质,右边的解一定全都是非法解。那么我们就应该去左边找解。
然后模拟这个跳石头的过程即可
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int len, n, m;
int s[N];
int d[N];
int sum[N];
int tempd[N];
int check(int mid)
{
for (int i = 0; i <= n + 1; i++)//两种实现方式,main中我用一个数组去记录距离,其实没有必要//第二种实现会更好
tempd[i] = d[i];
// cout << mid << endl;
int ans = 0;
for (int i = 1; i <= n + 1; i++)
{
// cout << tempd[i] <<' ';
if (tempd[i] < mid)
tempd[i + 1] += tempd[i], ans++;
if (ans > m)
return false;
}
return true;
}
int check1(int mid)
{
int ans = 0;
int i = 0;
int now = 0;
while (i < n + 1)
{
i++;
if (s[i] - s[now] < mid)
ans++;
else
now = i;
if (ans > m)
return false;
}
return true;
}
signed main()
{
cin >> len >> n >> m;
s[n + 1] = len;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
d[i] = s[i] - s[i - 1];
}
d[n + 1] = s[n + 1] - s[n];
// sort(d+1,d+n+1);
int l = 1;
int r = len;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (!check(mid))
r = mid - 1;
else
l = mid;
// cout << endl;
}
cout << l << endl;
}
- 因此最后总结,对于可以二分答案解决的问题,其所求答案必定满足有界性和单调性,即必定存在一个边界y,使得对于x<y<z,设边界为y,那么左右区间必定分属不同性质,满足不同条件,且整个大区间整体单调,因此可以用二分去寻找边界。
- 对于二分经常容易出现的边界问题,可以人为去赋予一个边界避免问题出现(减少特判考虑)