• 【ybtoj】二分算法例题


    【基础算法】第三章 二分算法

    例一 数列分段

    题目描述

    对于给定的一个长度为N的正整数数列A,现在将其分成M段,并要求每段连续,且每段和的最大值最小。

    输入格式

    第1行包含两个正整数N,M。
    第2行包含N个空格隔开的非负整数A。

    输出格式

    仅包含一个正整数,即每段和最大值最小为多少。

    样例输入

    5 3
    4 2 4 5 1

    样例输出

    6

    分析

    题中出现类似“最大值最小”的含义,这是答案具有单调性的最常见、最典型 的特征之一。设最优解为S,因为S的最优性如果要求每段和可以>S,那么一定存在一种划分方案使得总段数不超过M。因此答案就处于分段可行性的分界点上。

    样例代码

    bool check(int limit){
    int cnt=1;sum=0;
    for(int i=1;i<=n;i++){
    if(sum+a[i]<=n;i++){
    sum+=a[i];
    }
    else cnt++,sum=a[i];
    }
    return cnt<=m;
    }

    Code

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,a[100005],l,r,mid,ans;
    bool check(int x){
    int sum=0,num=0;
    for(int i=1;i<=n;i++)
    {
    if(sum+a[i]<=x)
    sum+=a[i];
    else sum=a[i],num++;
    }
    return num>=m;
    }
    int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>a[i],l=max(l,a[i]),r+=a[i];
    while(l<=r){
    mid=l+r>>1;
    if(check(mid))
    l=mid+1;
    else
    r=mid-1;
    }
    cout<<l;
    return 0;
    }

    例二 防具布置

    题目描述

    现在有N组防具。 我们可以认为防线是一维的,那么每一组防具都分布在防线的某一段上,并且同一组防具是等距离排列的。 也就是说,我们可以用三个整数 和 来描述一组防具,即这一组防具布置在防线的S,S+D,S+2D...S+KD位置上。 若一个位置上的防具数量为奇数,则我们称这个位置有破绽,但是整个防线上有且仅有一个位置有破绽或根本没有破绽。请你求出破绽的位置,或是确定防线没有破绽。

    输入格式

    第一行是一个整数T,表示有T组互相独立的测试数据。
    每组数据的第一行是一个整数N。
    之后N行,每行三个整数S,E,D,代表第i组防具的三个参数,数据用空格隔开。

    输出格式

    对于每组测试数据,如果防线没有破绽,输出一行 There's no weakness.。
    否则在一行内输出两个空格分隔的整数P和C,表示在P位置 有C个防具。当然C应该是奇数。

    样例输入

    3
    2
    1 10 1
    2 10 1
    2
    1 10 1
    1 10 1
    4
    1 10 1
    4 4 1
    1 5 1
    6 10 1

    样例输出

    1 1
    There's no weakness.
    4 3

    分析

    首先,若S(2^(31)-1)为偶数,则整道防线没有破绽。
    否则,设破绽的位置为P,故只有P上有奇数个防具,其他位置上都有偶数个,则对于x<p,S(x)均为偶数,对于x>=P,S(x)均为奇数。

    Code

    #include <bits/stdc++.h>
    #define N 200010
    using namespace std;
    int T, n;
    long long s[N], e[N], d[N], l, r, mid, ans;
    long long f(long long x){
    long long sum=0;
    for (int i=1;i<=n;i++)
    if (s[i]<=x)
    sum+=(min(x,e[i])-s[i])/d[i]+1;
    return sum;
    }
    void work ()
    {
    cin>>n;
    for (int i = 1; i <= n; i++)
    cin>>s[i]>>e[i]>>d[i];
    if (f((long long)2147483647)%2==0){
    printf("There's no weakness.\n");
    return;
    }
    l=ans=1,r=(long long)2147483647;
    while (l < r)
    {
    mid = (l + r) / 2;
    if (f(mid) % 2 == 1) r = mid;
    else l=mid+1;
    }
    cout<<r<<" "<<f(r)-f(r-1)<<endl;
    }
    int main()
    {
    cin>>T;
    while(T--)
    work ();
    return 0;
    }

    例三 最大均值

    题目描述

    给定正整数序列A,求一个平均数最大的,长度不小于L的(连续的)子段。

    输入格式

    第一行两个整数N和L。
    接下来N行,每行输入一个正整数A。

    输出格式

    输出一个整数,表示平均值的最大值乘以1000再向下取整之后得到的结果。

    样例输入

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

    样例输出

    6500

    分析

    注意到答案具有单调性,考虑二分答案,判定“是否存在一个长度不小于L的子段,平均值不小于mid”。
    如果把序列里每个数都减去二分的值,就进一步转化为“是否存在一个长度不小于L的子段,子段和非负”。
    然后只需要检查一下最大子段和是否为非负数,就可以确定二分上下界的变化范围了。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const double N=1e-5;
    int n,L;
    double a[100004],b[100004],sum[100004];
    bool check(double num){
    for(int i=1;i<=n;i++)
    b[i]=a[i]-num;
    for(int i=1;i<=n;i++)
    sum[i]=sum[i-1]+b[i];
    double ans=-1e10;
    double v=1e10;
    for(int i=L;i<=n;i++){
    v=min(v,sum[i-L]);
    ans=max(ans,sum[i]-v);
    }
    return ans>=0;
    }
    int main(){
    double l=-1e6,r=1e6;
    cin>>n>>L;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    while(l+N<r){
    double mid=(l+r)/2;
    if(check(mid))
    l=mid;
    else r=mid;
    }
    cout<<int (r*1000)<<endl;
    }
  • 相关阅读:
    2024年北京市安全员-C3证证考试题库及北京市安全员-C3证试题解析
    2022年服贸会:偶数科技参加Web3.0发展趋势高峰论坛
    目前为止 DAO靠什么盈利?
    使用dos命令符操作,感光屏绘图,ccd摄像头采集图像,并按程序进行机械加工的计算机
    FFmpeg安装教程
    【【萌新的FPGA学习之FIFO的介绍】】
    HTTP协议详解
    【音视频—基础】分辨率、码率和帧率
    【Java】若依的使用代码生成及字典的使用
    毅速3D打印丨哪些产品最适合应用3D打印随形水路模具
  • 原文地址:https://www.cnblogs.com/su-yichen/p/15912039.html