1. 程式人生 > >關於二分操作的基本應用

關於二分操作的基本應用

pty sed tin int nth data 包含 以及 輸入

前言:二分答案最重要的一點就是答案具有連續性,即有單調性的連續函數。

一:可以驗證答案是否正確,來改變答案區間

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

還可以用於某些去掉重復元素的操作。

這一類比較簡單,不做詳細解釋

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

如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\n");
    }
    else printf("%d\n",ans);
    return 0;
}
View Code

驗證的方法還有很多,目前遇到的只有這幾種,遇到後會再做補充。

關於二分操作的基本應用