本文用于记录个人算法竞赛学习,仅供参考
一.二分算法
二分算法一般用于具有二段性的问题,数据不一定具有单调性,所以单调可二分,可二分不一定就要单调。
二.整数二分
1. 模板一:将区间[l, r]划分为[l, mid] 和 [mid + 1, r], mid属于左区间,更新操作为r = mid 和 l = mid + 1; 计算mid 向下取整。
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
//check函数是处理逻辑
if (check(mid))
r = mid;
else
l = mid + 1;
}
//最后l和r都指向同一个数,返回哪个都可以
return l;
}
2.模板二:将区间[l, r]划分为[l ,mid - 1] 和 [mid + 1, r], mid 属于右区间,更新操作为r = mid - 1 和
l = mid; 计算mid时要+1向上取整避免死循环。
mid = (l + r + 1) >> 1避免死循环的逻辑是,假设剩余最后两个数,mid = l + r >> 1,那么mid = l,倘若mid对应值不是目标值,那么l = mid,这就形成闭环了,所以需要向上去取整。
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = (l + r + 1) >> 1;
//check函数是处理逻辑
if (check(mid))
l = mid;
else
r = mid - 1;
}
return l;
}
三.浮点数二分
double bsearch_3(double l, double r)
{
// eps 表示精度,取决于题目对精度的要求
const double eps = 1e-6;
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
四.题目:
1.浮点数二分
int main()
{
int n;
cin >> n;
//由于c++浮点数存在精确度问题,当题目要求精确到几位数时,一般esp再往后取多两位
double esp = 1e-8;
double l = 0, r = n;
while (r - l > esp)
{
double mid = (l + r) / 2;
if (mid * mid >= n)
r = mid;
else
l = mid;
}
printf("%lf\n", l);
return 0;
}
2.整数二分
P8647 [蓝桥杯 2017 省 AB] 分巧克力 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
根据题意得,需要找到符合条件的最大边长,假设这个边长是x,那么小于x的边长可以按要求分给每个小盆友(不是最大边长),大于x的边长不可能按要求平均分给小盆友,可以看出具有二段性,可以用二分,所以边长可以抽象成一条数轴,进而二分求得最大值。
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
int N = 100010;
int n, k;
int H[100010], W[100010];
bool check(int mid)
{
//统计饼干个数
LL cnt = 0;
for(int i = 0; i < n; i++)
{
cnt += (LL)(H[i] / mid) * (W[i] / mid);
}
if(cnt >= k)
return true;
else
return false;
}
int main()
{
scanf("%d %d",&n, &k);
for(int i = 0; i < n; i++)
{
scanf("%d %d",&H[i],&W[i]);
}
int l = 0, r = 100010;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid))
l = mid;
else
r = mid - 1;
}
printf("%d\n",r);
return 0;
}