洛谷能力提升綜合題單 Part 2.3 二分答案(仍為編輯完成)
阿新 • • 發佈:2021-09-22
#include<bits/stdc++.h> using namespace std; int j; double a,b,c,d,m; double fc(double x) { return a*x*x*x+b*x*x+c*x+d; } int main() { cin>>a>>b>>c>>d; for(double i=-100;i<=100;i++) { double l=i; double r=i+1; if(!fc(l)) { printf("%.2lf ",l); } if(fc(l)*fc(r)<0) { while(r-l>=0.001) { m=(l+r)/2; if(fc(l)*fc(m)<=0) { r=m; } else if(fc(l)*fc(m)>0) { l=m; } } printf("%.2lf ",r); j++; } if(j==3) { break; } } return 0; }
第一題思路非常明顯
在區間內,每次加1,根據性質若出現<0的,則對長度為1的區間進行二分。
二分的基礎用法是在單調序列或單調函式中進行查詢。
使用條件:單調性
複雜度:判定小於求解
整數域上:
1.終止邊界
2.左右區間取捨時的開閉情況
實數域:
1.精度
整數集合上的二分
最終答案處於[l,r]以內,迴圈以l==r結束,每次二分中間值mid會歸屬於左半段或右半段二者之一
在單調遞增序列a中查詢大於等於x的數中最小的一個
while(l<r) { int mid=(l+r)/2; if(a[mid]>=x) { r=mid; } else { l=mid+1; } } return a[l];
在單調遞增序列a中查詢小於等於x的數中最大的一個
while(l<r) { int mid=(l+r+1)/2; if(a[mid]<=x) { l=mid; } else { r=mid-1; } } return a[l];
注意到mid有兩種寫法1
1.
int mid=(l+r)/2; if(a[mid]>=x) { r=mid; } else { l=mid+1; }
2.
int mid=(l+r+1)/2; if(a[mid]<=x) { l=mid; } else { r=mid-1; }
認真觀察可知,二分核心是減小觀察區間。
對於第二段,若mid取l+r,極限情況r只比l大1的情況下,
若a[mid]<=x,mid==l,則區間並未縮小,進入死迴圈。
若走else,r<l,迴圈不能以r==l結束
分析還可知,mid=(l+r)/2取不到r,mid=(l+r+1)/2取不到l。
處理無解的情況,把最初的二分割槽間[1,n]擴大到[1,n+1]或[0,n],把a陣列一個越界的下標包含進來。
若二分終止於越界下標上,則說明a中不存在所求的數。
實數域上的二分
確定需要的精度eps,l+eps<r為迴圈條件,一般需要保留k位小數的時候,取eps=10^-(k+2)
while(l+1e-5<r) { double mid=(l+r)/2; if(calc(mid)) { r=mid; } else { l=mid; } }
若需要更高精度,可以固定迴圈次數的二分方法
for(int i=0;i<100;i++) { double mid=(l+r)/2; if(calc(mid)) { r=mid; } else { l=mid; } }
拓展:
三分求單峰函式極值
單峰函式:
擁有唯一的極大值點,極大值左側嚴格單調上升,右側嚴格單調下降
單谷函式:
或相反擁有唯一的極小值點。。。。。。
1.若f(lmid)<f(rmid),分析可知,可能兩點在極大值左邊/兩點在極大值左右,XXX一定
(書上似乎有錯誤,理解不能,先略過)
#include<bits/stdc++.h> using namespace std; int a[50001]; int l,n,m,mid,ans; int half(int x) { int s=0,num=0; for(int i=1;i<=n;i++) { if(a[i]-s<x) { num++; } else { s=a[i]; } } if(num>m) { return 0; } return 1; } int main() { /*while(l+1e-5<r) { double mid=(l+r)/2; if(calc(mid)) { r=mid; } else { l=mid; } }*/ /*for(int i=0;i<100;i++) { double mid=(l+r)/2; if(calc(mid)) { r=mid; } else { l=mid; } }*/ //int l,n,m; cin>>l>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; } int ll=0,rr=l; a[n+1]=l; while(ll<rr) { mid=(ll+rr+1)/2; if(half(mid)) { ll=mid; ans=mid; } else { rr=mid-1; } } cout<<ans; return 0; }
名師大將莫自牢,千軍萬馬避白袍