關於二分操作的基本應用
前言:二分答案最重要的一點就是答案具有連續性,即有單調性的連續函數。
一:可以驗證答案是否正確,來改變答案區間
如:求零點,求最接近元素。
還可以用於某些去掉重復元素的操作。
這一類比較簡單,不做詳細解釋
二:最大化最小值/最小化最大值
如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> #includeView Code<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; }
三:關於二分驗證的方法
1.按照題意模擬驗證
比較簡單不詳細說明
2.dp驗證
如:
魔棒
時間限制: 1 Sec 內存限制: 128 MB 提交: 50 解決: 8 [提交][狀態]題目描述
有一個英雄,初始生命值是hp(生命值無上限),在接下來的n秒內,每秒會受到一次傷害,第i秒受到的傷害值為a[i]。這個英雄有一個道具“魔杖”,魔杖的初始能量為0,每受到一次傷害,積攢一點能量。在英雄受到傷害後,可以立即釋放魔棒中的能量,恢復15*[能量點數]的生命值,且魔棒的點數清零。釋放能量有施法間隔cd(cd是正整數),即相鄰的兩次釋放的時間間隔至少有cd秒。任何時刻當hp<=0時視為死亡,問這個英雄存活下來的前提下,cd的值最大可以是多少?
註意,若a[i]為負,受到“傷害”後實際上生命值是增加的,魔棒仍然積攢能量。
輸入
第一行兩個正整數n,hp,含義如題目所述。
第二行n個整數,分別是a[1]..a[n]。
輸出
一個數,最大的cd,cd是一個正整數。
如果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
驗證的方法還有很多,目前遇到的只有這幾種,遇到後會再做補充。
關於二分操作的基本應用