文章目录
- 二分全套模板
-
- 一.🔍关键词
- 二.二分使用前提
- 三、整数二分模板
- 四、浮点数二分模板
- 五.经典例题
-
- [二分查找(一) - 计蒜客(https://www.jisuanke.com/problem/T1560)](#二分查找(一) - 计蒜客)
- [二分查找(二) - 计蒜客(https://www.jisuanke.com/problem/T1561)](#二分查找(二) - 计蒜客)
- [二分查找(三) - 计蒜客(https://www.jisuanke.com/problem/T1562)](#二分查找(三) - 计蒜客)
- [P1577 切绳子 - 洛谷(https://www.luogu.com.cn/problem/P1577)](#P1577 切绳子 - 洛谷)
- [P1824 \[USACO05FEB\\ 进击的奶牛 Aggressive Cows G - 洛谷](https://www.luogu.com.cn/problem/P1824)](#[P1824 USACO05FEB] 进击的奶牛 Aggressive Cows G - 洛谷)
- [P1843 奶牛晒衣服 - 洛谷(https://www.luogu.com.cn/problem/P1843)](#P1843 奶牛晒衣服 - 洛谷)
二分全套模板
整数二分分两类:闭区间
[l,r] while(l<=r)、左闭右开[l,r) while(l<r);浮点数二分固定循环次数 / 判断eps精度。
一.🔍关键词
| 高频关键词 | 二分类型 |
|---|---|
| 尽可能大的XX | 二分答案,求最大可行解(整数模板2 mid+1) |
| 尽可能小的XX | 二分答案,求最小可行解(整数模板1) |
| 最大值最小 | 二分答案,最小的最大值 |
| 最小值最大 | 二分答案,最大的最小值 |
| 切成k段、划分k组 | 二分答案 |
| 最少时间、最少代价 | 二分答案,求最小可行 |
| 保留小数,求最大/最小XX | 浮点数二分 |
二.二分使用前提
要么数组有序;要么答案区间上条件单调
三、整数二分模板
查询元素
适用:查找元素是否存在;闭区间找最小/最大可行答案(P1843奶牛晒衣服就是这套)
bool check(int x,int l,int r){
while(l<=r){
int mid=l+(r-l)/2;
if(a[mid]==x){
return true;
}else if(a[mid]>x){
r=mid-1;
}else{
l=mid+1;
}
}
return false;
}
闭区间求最小可行解
(找满足条件的最小t,例如晒衣服)
//check(mid):mid可行返回true
int l=1,r=max_val;
int ans=r; //ans初始为合法上界
while(l<=r)
{
int mid=l+(r-l)/2;
if(check(mid))
{
ans=mid;
r=mid-1; //可行,尝试更小
}
else
{
l=mid+1; //不可行,放大
}
}
//ans就是答案
闭区间求最大可行解
int l=1,r=max_val;
int ans=0;
while(l<=r)
{
int mid=l+(r-l)/2;
if(check(mid))
{
ans=mid;
l=mid+1; //可行,尝试更大
}
else
{
r=mid-1;
}
}
//ans为答案
第一个 ≥ x 的位置(lower_bound)
int check(int x,int l,int r){
int flag=-1;
while(l<=r){
int mid=l+(r-l)/2;
if(a[mid]>x){
r=mid-1;
flag=mid;
}
else{
l=mid+1;
}
}
return flag;
}
第一个 > x 的位置(upper_bound)
int check(int x,int l,int r){
int flag=-1;
while(l<=r){
int mid=l+(r-l)/2;
if(a[mid]>x){
r=mid-1;
flag=mid;
}
else{
l=mid+1;
}
}
return flag;
}
四、浮点数二分模板
(实数二分,比如P1577切绳子)
两种写法:①固定循环次数(推荐,精度稳定,不怕eps调错);②eps判断。
写法1:固定循环100次
循环100次,精度可以达到 10 − 30 10^{-30} 10−30,远超题目要求。
double l=0, r=1e9;
//循环100次足够
for(int i=1;i<=100;i++)
{
double mid = (l + r)/2;
if(check(mid))
{
//看题意:如果可行想要更大,l=mid;想要更小 r=mid
l=mid;
}
else
{
r=mid;
}
}
//输出l或者r
printf("%.2f\n",l);
写法2:eps精度控制
const double eps=1e-4; //输出保留2位,eps取1e‑4或1e‑5,比输出精度多2位
double l=0,r=1e9;
while(r - l > eps)
{
double mid=(l+r)/2;
if(check(mid))
{
l=mid;
}
else
{
r=mid;
}
}
printf("%.2f\n",l);
⚠️重点:如果题目要求输出k位小数,
eps设置要比输出精度小2个数量级。例:输出2位小数 → eps=1e‑4;输出6位 → eps=1e‑8。