The Festival of Insi|

hsy2093

园龄:1年5个月粉丝:0关注:0

二分算法习题汇总

二分算法习题汇总

一、复制书稿

题目描述

现在要把 m 本有顺序的书分给 k 个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。

现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

输入格式

第一行两个整数 m,k

第二行 m 个整数,第 i 个整数表示第 i 本书的页数。

输出格式

共1行:分配给抄写员的页数的最大值

样例 #1

样例输入 #1

9 3
1 2 3 4 5 6 7 8 9

样例输出 #1

17

提示

1km500

代码实现

#include <stdio.h>
#include <iostream>
#include <math.h>
#include <algorithm>
#include <cstring>
using namespace std;
int M, K;
int page[505];

//判别答案合法性
bool check(int num){
    int group = 1, rest = num;
    for(int i = 0; i < M; i++){
        //如果这个人当前可以抄的上限页数大于当前这本书书的页数,就交给他抄,不用新增人。
        if(rest >= page[i]) rest -= page[i];
        //如果这个人抄不了这本书,就要再来一个人,并且更新他的上限页数。
        else{
            group ++;
            rest = num - page[i];
            if(rest < 0)
                return false;
        }
    }
    //group表示最少需要多少个人
    return group <= K;
}

int main(){
    cin >> M >> K;
    int sum = 0;
    int l = 0, r, mid;
    for(int i = 0; i < M; i++){
        cin >> page[i];
        sum += page[i];
    }
    r = sum;
    while(l < r){
        mid = (l + r) >> 1;
        //如果mid合法,则在左区间寻找答案
        if(check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l << endl;
}

二、 青蛙(frog)

题目描述

小 L 向一所小学捐赠了一些青蛙,这些青蛙一共有 M 种品种,每只青蛙都属于一种品种。

老师需要把所有的青蛙分给 N 个孩子。每个孩子得到的所有青蛙都必须属于相同的品种,而且可以有一些孩子一只青蛙也没得到。

我们把怄火值定义为得到青蛙最多的孩子得到的青蛙数量。请你帮助老师分青蛙,使得怄火值最小

例如,如果有 4 只红品种的青蛙(RRRR)和 7 个蓝品种的青蛙(BBBBBBB),要分给 5 个孩子,

那么我们可以这样划分:RRRRBBBBBBB。这样分的怄火值为 3,是最小的。

输入格式

第一行两个正整数,N,M,含义如题目所示。

第二行 M 个整数,表示第 M 个品种的青蛙有几只,保证每个品种的青蛙的数量都在 [1,109] 中。

输出格式

一行一个整数,表示最小的怄火值。

样例 #1

样例输入 #1

7 4
1 2 3 4

样例输出 #1

2

提示

对于 20% 的数据,保证 1M10

对于另外 30% 的数据,保证 1M10001N10000

对于 100% 的数据,保证 1M3×1051N109MN

代码实现

#include <stdio.h>
#include <iostream>
using namespace std;
int a[300005];
int n, m;

bool check(int mid){
    int cnt = 0;
    for(int i = 1; i <= m; i++){
        if(a[i] % mid == 0)     cnt += (a[i]/mid);
        else    cnt += (a[i]/mid+1);
        if(cnt > n)     return false;
    }
    return true;
}

int main() {
    int maxx = 0;
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        cin >> a[i];
        maxx = max(maxx, a[i]);
    }
    int l = 1, r = maxx;
    while(l < r){
        int mid = (l+r) / 2;
        if(check(mid))    r = mid;
        else    l = mid + 1;
    }
    cout << r << endl;
}

三、平均数

题目描述

给一个长度为 n 的数列,我们需要找出该数列的一个子串,使得子串平均数最大化,并且子串长度 m

输入格式

第一行两个整数 nm

接下来 n 行,每行一个整数 ai,表示序列第 i 个数字。

输出格式

一个整数,表示最大平均数的 1000 倍,如果末尾有小数,直接舍去,不要用四舍五入求整。

样例 #1

样例输入 #1

10 6
6
4
2
10
3
8
5
9
4
1

样例输出 #1

6500

提示

数据规模与约定

  • 对于 60% 的数据,保证 mn104
  • 对于 100% 的数据,保证 1mn1050ai2000

代码实现

#include <stdio.h>
#include <iostream>
using namespace std;
long long a[100005], sum[100005];
long long n, m, maxx = 0;
bool check(long long mid){
    long long minn = 1e15;
    for(int i = 1; i <= n; i++){
        sum[i] = sum[i-1] + a[i] - mid;
        if(i >= m){
            //可能为一个负值
            minn = min(minn, sum[i-m]);
            if(sum[i] >= minn)  return true;
        }
    }
    return false;
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        a[i] *= 10000;
        if(maxx < a[i]) maxx = a[i];
    }
    long long l = 0, r = maxx, mid;
    while(l < r){
        mid = (l+r+1) / 2;
        if(check(mid))  l = mid;
        else       r = mid-1;
    }
    cout << l / 10 << endl;
    return 0;
}

四、查询(query)

题目描述

青蛙老师有三个长度为 n 的数组 a,b,c

给定一个正整数 k,他想知道在所有 n2 个二元组 (i,j)(1in,1jn) 中,aj+bj×ci 的第 k 小值是多少。

但是他不会做,于是将问题交给你了。

输入格式

第一行一个正整数 n,表示数组长度。

第二行 n 个正整数,依次表示 a1,a2,an

第三行 n 个正整数,依次表示 b1,b2,bn

第四行 n 个正整数,依次表示 c1,c2,cn

接下来一行,一个正整数 k,含义如题所述。

输出格式

一行一个整数表示答案。

样例#1

样例输入#1

5
1 3 6 4 1
3 8 9 2 6
5 6 5 3 2
10

样例输出#1

16

数据范围

对于 16% 的数据,满足 n100,1ai,bi,ci1000

对于另外 16% 的数据,满足 n1000

对于另外 16% 的数据,满足 kn,1ai,bi,ci1000

对于另外 24% 的数据,满足 kn

对于 100% 的数据,满足 n105,1kn2,1ai,bi,ci109

代码实现

//query.cpp
#include <stdio.h>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
ll a[100005], b[100005], c[100005];
ll n, k;

bool check(ll x){
    ll cnt = 0;
    //当固定i时,有多少个c满足乘积结果小于x的情况
    for(int i = 1; i <= n; i++){
        ll l = 0, r = n;
        while(l < r){
            ll mid = (l + r + 1) / 2;
            if(a[i] + b[i] * c[mid] >= x)    r = mid-1;
            else    l = mid;
        }
        cnt += l;
    }
    if(cnt >= k)     return true;
    else        return false;
}

int main(){
    cin >> n;
    for(int i = 1; i <= n; i++)     cin >> a[i];
    for(int i = 1; i <= n; i++)     cin >> b[i];
    for(int i = 1; i <= n; i++)     cin >> c[i];
    cin >> k;
    sort(c+1, c+n+1);
    ll l = 0, r = 1e19;
    //固定i,找对应的j
    while(l < r){
        ll mid = (l + r + 1) / 2;
        //check合法证明序列中有k个数或大于k个数小于mid,所以mid应该往更小的地方找
        if(check(mid))          r = mid - 1;
        //否则要往大了找
        else    l = mid;
    }
    cout << r << endl;
    return 0;
}

本文作者:hsy2093

本文链接:https://www.cnblogs.com/hsy2093/p/17796564.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hsy2093  阅读(45)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起