• 关于二分操作的基本应用


    前言:二分答案最重要的一点就是答案具有连续性,即有单调性的连续函数。

    一:可以验证答案是否正确,来改变答案区间

    如:求零点,求最接近元素。

    还可以用于某些去掉重复元素的操作。

    这一类比较简单,不做详细解释

    二:最大化最小值/最小化最大值

    如noip2015:

    2257: [NOIP2015]跳石头

    Description

    一年一度的“跳石头”比赛又要开始了! 

    这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。 

    为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M块岩石(不能移走起点和终点的岩石)。

    Input

    输入第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。  接下来N行,每行一个整数,第i行的整数Di(0 < Di < L)表示第i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

    Output

    输出只包含一个整数,即最短跳跃距离的最大值。 

    Sample Input

    25 5 2
    2
    11
    14
    17
    21

    Sample Output

    4

    HINT

    样例说明】  将与起点距离为2和14的两个岩石移走后,最短的跳跃距离为4(从与起点距离17的岩石跳到距离21的岩石,或者从距离21的岩石跳到终点)。 

    【数据规模与约定】  对于20%的数据,0 ≤ M ≤ N ≤ 10。  对于50%的数据,0 ≤ M ≤ N ≤ 100。  对于100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

    下面代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int lenth[50001];
    int len,n,m;
    bool check(int step)
    {
        int count=0,temp=0,i;
        for(i=1;i<=n;i++)
        {
            if(lenth[i]-lenth[temp]>=step)
            {
                temp=i;
            }
            else count++;
        }
        if(count>m) 
        return false;
        if(len-lenth[temp]<step) 
        return false;
        return true;
    }
    int main()
    {
        int i,j;
        scanf("%d%d%d",&len,&n,&m);
        int l=0,r,mid;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&lenth[i]);
        }
        r=len;
        while(l+1<r)
        {
            mid=(l+r)/2;
            if(check(mid)) 
            l=mid;
            else r=mid;
        }
        if(check(r))
        cout<<r;
        else
        cout<<l;
         
    }
    View Code


    三:关于二分验证的方法

    1.按照题意模拟验证

    比较简单不详细说明

    2.dp验证

    如:

     魔棒

    时间限制: 1 Sec  内存限制: 128 MB 提交: 50  解决: 8 [提交][状态]

    题目描述

    有一个英雄,初始生命值是hp(生命值无上限),在接下来的n秒内,每秒会受到一次伤害,第i秒受到的伤害值为a[i]。这个英雄有一个道具“魔杖”,魔杖的初始能量为0,每受到一次伤害,积攒一点能量。在英雄受到伤害后,可以立即释放魔棒中的能量,恢复15*[能量点数]的生命值,且魔棒的点数清零。释放能量有施法间隔cdcd是正整数),即相邻的两次释放的时间间隔至少有cd秒。任何时刻当hp<=0时视为死亡,问这个英雄存活下来的前提下,cd的值最大可以是多少?

        注意,若a[i]为负,受到“伤害”后实际上生命值是增加的,魔棒仍然积攒能量。

    输入

    第一行两个正整数n,hp,含义如题目所述。

    第二行n个整数,分别是a[1]..a[n]

    输出

    一个数,最大的cdcd是一个正整数。

    如果cd没有上限,输出“No upper bound.”;如果无论如何都不能存活,输出-1

    样例输入

    7 30
    20 5 30 4 10 5 20

    样例输出

    2

    提示

    对于30%的数据,n<=12

    对于100%的数据,n<=500|a[i]|<=1000

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    ll a[550],hp,dp[501][501][2],sp[501][2],n;
    bool check(int mid)
    {
        memset(dp,0,sizeof(dp));
        memset(sp,0,sizeof(sp));
        dp[0][0][0]=hp;
        sp[0][0]=hp;
        int i,j;
        for(i=1;i<=n;i++)
        {
            for(j=2;j<=i;j++)
            {
                dp[i][j][0]=dp[i-1][j-1][0]-a[i];
                dp[i][j][1]=dp[i-1][j-1][1]-a[i];
                if(dp[i][j][0]>0)
                {
                    sp[i][0]=max(sp[i][0],dp[i][j][0]+j*15);
                }
                if(dp[i][j][1]>0&&j>=mid)
                {
                    sp[i][1]=max(sp[i][1],dp[i][j][1]+j*15);
                }
            }
            dp[i][1][0]=dp[i-1][0][0]-a[i];
            if(dp[i][1][0]>0)
            {
                sp[i][0]=max(sp[i][0],dp[i][1][0]+15);
            }
            int step=max(sp[i-1][0],sp[i-1][1]);
            if(step>=a[i])
            {
                dp[i][0][1]=step+15-a[i];
            }
            dp[i][1][1]=step-a[i];
        }
        for(i=0;i<=n;i++)
        {
            if(dp[n][i][0]>0||dp[n][i][1]>0)
            return 1;
        }
        return 0;
    }
    int main()
    {
        ll i,j;
        ll count=0;
        int step=0;
        scanf("%lld%lld",&n,&hp);
        ll ppp=hp;
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
        }
        hp=ppp;
        for(i=1;i<=n;i++)
        {
            hp=hp-a[i];
            count++;
            if(hp<=0&&step==0)
            {
                hp=hp+(count*15);
                step=1;
            }
            if(hp>0&&i==n)
            {
                cout<<"No upper bound.";
                return 0;
            }
        }
        hp=ppp;
        ll l=0,r=n,mid;
        while(l+1<r)
        {
            mid=(l+r)/2;
            if(check(mid)==1)
            {
                l=mid;
            }
            if(check(mid)==0)
            {
                r=mid;
            }
        }
        if(l==0)
        cout<<"-1";
        else
        cout<<l;
    }
    View Code


    dp验证比较常见,不再概述

    3.最短路验证

    问题 A: 收费站(cost.pas/c/cpp)

    题目描述

        在某个遥远的国家里,有n个城市。编号为1,2,3,……,n。
        这个国家的政府修建了m条双向的公路。每条公路连接着两个城市。沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。
        开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。所有的收费站都在城市中,在城市间的公路上没有任何的收费站。
        小红现在要开车从城市u到城市v(1<=u,v<=n)。她的车最多可以装下s升的汽油。在出发的时候,车的油箱是满的,并且她在路上不想加油。
        在路上,每经过一个城市,她要交一定的费用。如果她某次交的费用比较多,她的心情就会变得很糟。所以她想知道,在她能到达目的地的前提下,她交的费用中最多的一次最少是多少。这个问题对于她来说太难了,于是她找到了聪明的你,你能帮帮她吗?

    输入

        第一行5个正整数,n,m,u,v,s。分别表示有n个城市,m条公路,从城市u到城市v,车的油箱的容量为s升。

        接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。

        再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=n)。表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,需要用ci升汽油。

    输出

        仅一个整数,表示小红交费最多的一次的最小值。

        如果她无法到达城市v,输出-1。

    样例输入

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

    样例输出

    8
    

    提示

        对于60%的数据,满足n<=200,m<=10000,s<=200

        对于100%的数据,满足n<=10000,m<=50000,s<=1000000000

        对于100%的数据,满足ci<=1000000000,fi<=1000000000,可能有两条边连接着相同的城市。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define inf 0X3F3F3F3F
    using namespace std;
     
    int n,m,u,v,s,f[10005],dist[10005];
    vector<int>g[10005],w[10005];
     
    struct data
    {
        int d,id;
        friend bool operator < (data a,data b)
        {
            return a.d>b.d;
        }
    };
     
    void Dij(int s,int *d,int limit)
    {
        priority_queue<data>pq;
        for(int i=1;i<=n;i++) d[i]=inf;
        pq.push((data){0,s});
        d[s]=0;
     
        while(!pq.empty())
        {
            data t=pq.top(); pq.pop();
            int i=t.id;
            if(f[i]>limit) continue;
            if(t.d>d[i]) continue;
            d[i]=t.d;
     
            for(int k=0;k<g[i].size();k++)
            {
                int j=g[i][k],c=w[i][k];
                if(f[j]>limit) continue;
                if(d[i]+c<d[j])
                {
                    d[j]=d[i]+c;
                    pq.push((data){d[j],j});
                }
            }
        }
    }
     
    int main()
    {
        scanf("%d%d%d%d%d",&n,&m,&u,&v,&s);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&f[i]);
        }
     
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            g[x].push_back(y);
            g[y].push_back(x);
            w[x].push_back(z);
            w[y].push_back(z);
        }
     
        int A=0,B=1000000000,ans;
        while(A<=B)
        {
            int mid=(A+B)/2;
            Dij(u,dist,mid);
            if(dist[v]<=s)
            {
                B=mid-1;
                ans=mid;
            }
            else
            {
                A=mid+1;
            }
        }
        if(B==1000000000)
        {
            printf("-1
    ");
        }
        else printf("%d
    ",ans);
        return 0;
    }
    View Code

    验证的方法还有很多,目前遇到的只有这几种,遇到后会再做补充。

  • 相关阅读:
    KINavigationController使用演示例子
    基于STC12C5A的MINI3216多功能点阵时钟
    Android开发SDK接入机智云,智能家居实现APP远程控制多设备
    自定义通用dialogFragment
    获取取并下载tuku的漫画的爬虫
    Mvp快速搭建商城购物车模块
    仿360手机助手下载按钮
    Pythond 读写HDF5文件
    python tricks —— datetime 删除日期中的前导 0
    天文网站
  • 原文地址:https://www.cnblogs.com/ashon37w/p/7197207.html
Copyright © 2020-2023  润新知