zoukankan      html  css  js  c++  java
  • 关于二分操作的基本应用

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

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

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

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

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

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

    如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

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

  • 相关阅读:
    11111 Generalized Matrioshkas
    Uva 442 Matrix Chain Multiplication
    Uva 10815 Andy's First Dictionary
    Uva 537 Artificial Intelligence?
    Uva 340 MasterMind Hints
    SCAU 9508 诸葛给我牌(水泥题)
    Uva 10420 List of Conquests(排序水题)
    Uva 409 Excuses, Excuses!
    10/26
    11/2
  • 原文地址:https://www.cnblogs.com/ashon37w/p/7197207.html
Copyright © 2011-2022 走看看