个人的理解:众多周知,二分是一分为二。二分分为 二分查找 和 二分答案 。二分查找的话会更加容易理解一些,直接利用二分进行寻找要找的答案;二分答案是利用二分间接的寻找要求的结果。
同样的,二分 分为 整数二分 ,浮点二分 。
此外,二分的话要特别注意边界的判断,判断错的话可能会出现 可能会超时 ,运行错误 的问题。
自己积累的模板
习题
一:二分查找的模板题
题目大意
n个数,q次询问,给出要查询的数,问该数的第一次出现的位置和最后一次出现的位置,未找到则输出 -1 -1
解题思路
1.思考要用到的二分的边界
2.思考二分中间的范围变换应该是怎样的
坑点
1.无
算法1:二分
实现步骤
1.读入后,m次循环判断 左右边界的变化
2.最后判断要输出的是哪个端点
3.最后输出结果
代码
#include<bits/stdc++.h>
using namespace std;
int n,q,k;
const int N=1e5+10;
int a[N];
int main()
{
cin>>n>>q;
for(int i=0;i<n;i++)
{
cin>>a[i];//依次读入 n 个数
}
while(q--)//q 次询问
{
cin>>k;//读入要查询的数
int l=0,r=n-1;//左右端点的范围
while(l<r)//左端点
{
int mid = l + r >> 1;//中点
if(a[mid]>=k)//超出左端点的范围
{
r=mid;//将右端变为 mid 进行缩小范围 ,继续判断
}
else//未达到左端点
{
l=mid+1;//将 l 变为 mid +1 进行扩大范围,继续判断
}
}
if(a[l]!=k)//未找到左端点
{
cout<<"-1 -1";//直接输出两个 -1
}
else//找到左端点
{
cout<<l<<" ";//输出左端点
l=0,r=n-1;//左右端点的范围
while(l<r)
{
int mid= l + r +1 >>1;//为了避免死循环
if(a[mid]<=k)//右端点
{
l=mid;//不满足右端点,要向右扩大范围,进行判断
}
else{
r=mid-1;//mid 超出范围 ,利用 r= mid -1 进行缩小范围
}
}
cout<<l<<endl;
}
}
return 0;
}
二:二分答案
P7585 [COCI2012-2013#1] LJUBOMORA
题目大意
给你孩子数,弹珠种类数,让你求出最小的嫉妒值
解题思路
1.首先看到题目感觉和二分没什么联系,但看一眼题目标签发现有二分
2.接着判断是查找还是答案
3.判断二分的边界,范围的变化
坑点
1.题中数的范围较大,所以要开 long long
算法:二分答案
实现步骤
1.根据二分答案的模板,所以需要一个函数进行检测是否符合二分的要求
2.然后再主函数里直接利用该函数进行检测中点是否符合题中要求
3.接着判断二分的范围变化
4.最终输出结果
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 300000 + 10;
ll n, m, l, r, ans, mid, a[N];//根据题意 ,数据需要开 longlong
bool check(ll x)//利用 check 函数 求出每种颜色可得人数
{
ll sum = 0;//用 sum 来记录
for(int i=1;i<=m;i++)
{
sum += a[i] / x;//记录 人数
if(a[i] % x != 0)//有余数的情况
{
sum++;//需要另+1 ,因为该题符合向上取整
}
}
return sum <= n;//人数是否达标
}
int main()
{
cin >> n >> m;//读入n个孩子 ,m 种弹珠
for(int i=1;i<=m;i++)
{
cin >> a[i];
r += a[i];//确定右边界,嫉妒值极限为所有球之和
}
while(l <= r)
{//开始二分
mid = (l + r) >> 1;//中间 值 >> 等价于 /2 ,因为 + 的优先级高于 >> 所以无需加括号
if(check(mid))
{ //返回true意味着可以考虑更大些
r = mid - 1;//返回 true意味着要大些 表示 mid>=要求的点,向左移动 ,缩小范围
ans = mid;//将mid 赋给 ans
}else//相反 mid <要求的点
{
l = mid + 1;//返回false意味着要小些 需要向右移动,扩大范围
}
}
cout << ans << endl;//输出答案
return 0;
}