(一)二分模板
二分可以说是蓝桥杯的必考点,是基础算法,是蓝桥杯的送分题
先是二分一般的模板,忘了自己推一推,实在不行就一个一个试
//一.浮点数二分,不用考虑边界情况,
while (l - r <= 1e-8) {
double mid = (l + r)/ 2;
if (sum(mid) >= x) r = mid;
else l = mid;
}
//二.实数二分
//1.二分函数的使用
//(1) 找到第一个 >= x的位置:lower_bound(a + 1,a + n + 1,x) - a
//(2) 找到第一个 > x的位置:upper_bound(a + 1,a + n + 1,x) - a
//例如:a[10] = {0,1,2,3,4,5,5,6,7,8};
int l = lower_bound(a,a + 10,5) - a;
int r = upper_bound(a,a + 10,5) - a;
l = 5,第一个5的位置
r = 7,第二个5后面一个位置
// 2.手写二分
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
(二)蓝桥杯二分例题
T1:分巧克力(蓝桥杯17年省赛)
解题思路:
二分巧克力的边长即可,即二分答案
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long ;
const int N = 1e5 + 50;
int a[N];
int b[N];
int n,k;
bool check(int x) { // 统计能切出几块
int ans = 0;
for (int i = 1;i <= n; i++) {
ans += (a[i]/x)*(b[i]/x);
}
return ans >= k;
}
int main() {
cin >> n >> k;
for (int i = 1;i <= n; i++) cin >> a[i] >> b[i];
int l = 0,r = 1e5;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l;
}
T2 123(蓝桥杯21年国赛)
解题思路:
1.暴力:直接将前1e6项的元素存入到数组中,然后计算前缀和,直接查询答案即可,实在没有思路可以暴力骗分,大概可以的40%的分
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long ;
const int N = 1e7 + 50;
int a[N];
int main() {
int n; cin >> n;
int cnt = 0;
for (int s = 1;cnt <= N;s++) {
for (int i = 1;i <= s; i++) {
a[++cnt] = i;
if (cnt > N) break;
}
}
a[0] = 0;
for (int i = 1;i <= N; i++) a[i] += a[i - 1];
while (n--) {
int l,r; cin >> l >> r;
cout << a[r] - a[l - 1] << '\n';
}
return 0;
}
2.正解:
T3.卡牌(蓝桥杯22年国赛)
解题思路:
二分答案,二分牌的套数
check函数考虑使用的空白牌数是否超过限制,每种牌的手写次数是否超过限制
代码:
// 思路:二分答案,牌的套数
// 应该开long long,m<=4e10,
#include <bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long ;
const int N = 2e5 + 50;
int n,m;
int a[N],b[N];
bool check(int x) {
int cnt = 0; // 用掉的空白牌数
for (int i = 1;i <= n; i++) {
if (a[i] < x) { // 需要用空白牌
if (x-a[i] > b[i]) return false;
cnt += x - a[i]; // 记录用的空白牌数
}
}
return cnt<=m;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
int r = -1;
for (int i = 1;i <= n; i++) {
cin >> a[i];
r = max(r,a[i]);
}
for (int j = 1;j <= n; j++) cin >> b[j];
int l = 0;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << r;
return 0;
}
T4 扫地机器人(蓝桥杯19年省赛)
解题思路:
1.所有机器人归位的最少花费时间取决于花费时间最长的那个机器人
2.所以我们要求的就是机器人归位花费最长时间的最小值
3.如果一个机器人要打扫长度为len的区间,那么归位时间为(len - 1) * 2,那么我们就可以二分一个机器人打扫长度,是每个机器人打扫该长度能打扫完整块区域的最小长度,因而花费的时间也就是最小的
4.check函数,对于一个机器人,不同的清扫长度就有三种情况:
用sweep表示当前扫到第几格了,len为一个机器人扫的长度,x表示当前机器人的位置
<1>当前机器人扫不到前面的某些区域,长度过短,直接返回false
只有绿色的地方能被扫到
<2> 要扫当前机器人前面的地方,sweep += len
绿色为清扫的地方
<3>不用扫当前机器人前面的地方,sweep = x + len - 1
绿色为清扫的地方
代码:
// 思路:所有机器人归位的最少花费时间取决于花费时间最长的那个机器人
// 所以我们要求的就是归位花费最长时间的最小值
// 由此我们可以用二分来解决该问题
// 我们二分一个机器人能一次打扫的最小长度len,花费的时间就为(len - 1)*2
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 50;
int robot[N]; // 记录每个机器人的位置
int n,k;
bool check(int len) {
int sweep = 0; // 记录当前清扫到第几格了
for (int i = 1;i <= k ;i++) {
if (robot[i] - len > sweep) return false; // 1.当前长度过短
if (sweep <= robot[i]) sweep += len; // 2.要扫前面
else sweep = robot[i] + len - 1; // 3.不用扫前面
}
return sweep >= n; // 是否扫完了所有区域
}
int main() {
cin >> n >> k;
for (int i = 1;i <= n;i++) cin >> robot[i];
sort(robot + 1,robot + k + 1);
int l = 0,r = N;
// 二分最小长度
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << (l - 1) * 2; // 最小花费时间
return 0;
}