二分答案训练记录

  • 题目1:P8647 [蓝桥杯 2017 省 AB] 分巧克力

 

思路一:分得巧克力的最小边长为1(题目保证),最大可能为1e5(数据限制)在这个区间内,答案是单调的。原因:若x边长不可行,则大于x的边长都不可行。因此考虑二分答案做法的方向。

思路二:对于最终分边长为x巧克力是否可行的check函数如何写?从“从已知长宽为h[i],w[i]的这些巧克力里去尽量多分边长为x的正方形巧克力”出发,可得贪心写法。每一块巧克力对最终的正方形巧克力的贡献为

sum += (h[i] / x) * (w[i] / x);

check函数顺序枚举所有长方形巧克力即可,sum大于所需值则分边长为x的正方形巧克力是可行方案

综上得出二分答案写法 考虑时间复杂度是否可行

二分复杂度 log(1e5)

check复杂度——n

总——nlog(1e5)

可行 于是二分答案写出代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cmath>
#include<cstring>
#include<list>
#define ll long long
#define pii pair<int,int>
using namespace std;

const int MAXN = 1e5 + 10;
int n, k;//巧克力块数 要分的块数
int h[MAXN], w[MAXN];


bool check(int x) {
    //验证边长为x的巧克力能否满足块数为k
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += (h[i] / x) * (w[i] / x);
    }
    if (sum >= k)return 1;
    else return 0;
}

int main(){
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> h[i] >> w[i];
    }
    int l = 1, r = 1e5;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid)) {
            //当下边长可行 尝试更大块的巧克力
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }
    cout << r;
    return 0;
}
  • 题目2:[ABC144E] Gluttony

思路一:成绩是队员耗时的最大值,成绩最好,即最大值最小化,联想到二分答案。(单调性:某耗时可行,更长耗时必然可行;,某一耗时不可行,更短耗时必不可行。)

思路二:check函数写法  

在不考虑修行情况下,a序列最小值和f序列最大值依次乘积是最优方案。将修行纳入考虑,若一队员耗时 tmp 超过了要求耗时,则将需要的修行记录。需要的修行总和小于k(最多修行时长)则可行。

综上得出二分答案写法:

注意用向下取整实现向上取整的方法

 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cmath>
#include<cstring>
#include<list>
#define ll long long
#define pii pair<int,int>
using namespace std;

const int MAXN = 2e5 + 10;
ll n, k;//队员数  修行次数
ll a[MAXN], f[MAXN];//消化代价 难吃程度

bool check(ll x) {
    ll cnt = 0;
    for (int i = 0; i < n; i++) {
        ll tmp = a[i] * f[n - i-1];
        if (tmp > x) {
            //需要修行的次数
            cnt += (a[i] * f[n - i - 1] - x + f[n - i - 1] - 1) / f[n - i - 1];
        }
    }
    if (cnt > k)return 0;
    return 1;
}

int main(){
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> a[i] >> f[i];
    }
    sort(a, a + n);
    sort(f, f + n);
    ll l = 0, r = 1e12+10;
    while (l <= r) {
        ll mid = (l + r) / 2;
        if (check(mid)) {
            //成绩合理 尝试更好成绩
            r = mid - 1;
        }
        else l = mid + 1;
    }
    cout << l;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值