蓝桥杯备赛-二分专题

(一)二分模板

二分可以说是蓝桥杯的必考点,是基础算法,是蓝桥杯的送分题

先是二分一般的模板,忘了自己推一推,实在不行就一个一个试

//一.浮点数二分,不用考虑边界情况,
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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值