二分法:
在看這個視頻前,我對於二分法是一頭霧水的,又加上這個算法我個人很容易寫錯emm...。視頻提到ACwing上的一道題,我用自以為聰明的方法去做,結果TLE了,實在丟人,不說了,開整!
對於例題 789:數的范圍,尋找一個數前后第一次與最后一次出現的坐標。我們需要這個模板:
數組定為number[];
(1)來看第一種情況:如圖,假設兩個點分別是最先與最后出現的位置。求第一次x出現的位置實際上就是(1)這種情況。那么我們定一個條件
mid=(l+r)>>1 if(number[mid]在a中)
r=mid; 區間變為[L,mid], 因為mid處可能是答案,所以mid不要加一也不要減一。
else(落在b中)
l=mid+1; 區間變為[mid+1,R]
(2)來看第二種情況:如圖,就是求x最后一次出現的位置了。依然是
int mid=(l+r+1)>>1; if(number[mid]在b中)
l=mid 區間變為[mid,R],mid不要動因為mid處可能是答案
else
r=mid-1 因為mid處肯定不是答案,所以要減一,區間變為[L,mid-1];
但是注意要注意(2)中的(L+r+1)>>1,因為如果 l=r-1時,式子不加一會出現mid=L+1/2,因為向下取整,所以mid=L,進入if后會一直求出區間[L,R]造成死循環,而(1)就不會出現。
綜上,有以下兩個模板,分別對應不同的情況
//區間[L,R]被分成[L,mid]和[mid+1,R]時
int bsearch_1(int l,int r) { while(l<r) { int mid=l+r >>1; if(check(mid)) r=mid; else l=mid+1; } }
//區間[L,R]被分成[L,mid-1]和[mid,R]時
int bsearch_2(int l,int r)
{
while(l<r)
{
int mid=l+r+1 >>1;
if(check(mid))
l=mid;
else
r=mid-1; }
}
然后上789代碼 :
#include<iostream> const int maxn=1e5+10; using namespace std; int a[maxn]; int n,k; int main() { cin>>n>>k; for(int i=0;i<n;i++) cin>>a[i]; while(k--) { int x; cin>>x; int l=0,r=n-1; while(l<r) { int mid=(l+r)>>1; if(a[mid]>=x) { r=mid; } else { l=mid+1; } } if(a[l]!=x) cout<<"-1 -1"<<endl; else { cout<<l<<' '; int l=0,r=n-1; while(l<r) { int mid=(l+r+1)>>1; if(a[mid]<=x) { l=mid; } else r=mid-1; } cout<<r<<endl; } } }
再來個手動開方嘿嘿,二分法:
#include<iostream> using namespace std; #include<cstdio> int main() { double x; while(cin>>x) { double l=0,r=x; while((r-l)>1e-6)//精度不夠再加,可以時1e-8 { double mid=(l+r)/2; if(mid*mid>x) r=mid; else l=mid; } printf("%lf\n",l); } }
開三次方:ACWING 790
#include<iostream> #include<cmath> #include<cstdio> using namespace std; int main() { double x; while(cin>>x) { double l,r; if(x>=0) l=0,r=x; else l=x,r=0; while(fabs(r-l)>1e-8) { double mid = (l+r)/2; if(mid*mid*mid>x) r=mid; else l=mid; } printf("%lf\n",l); } }