python中的反射機制
阿新 • • 發佈:2022-05-17
日期:2022年5月19日
注:本部落格僅供參考
概念與基本思路
二分演算法是一種在有序陣列中查詢某一特定元素的查詢演算法。查詢過程從陣列的中間元素開始:如果中間元素正好是要查詢的元素,則查詢過程結束;如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。
與分治的關係
二分可以看作為一種特殊的分治
要求
資料一定要是有序的(單調的),不能亂序。
實現方法
用L(left)和R(right)代表某一區域的範圍。L與R相加除以二計算出中間值mid的值,若答案在mid的左邊,則R=mid-1;若答案在mid的右邊,則L=mid。重複這個過程,直到L與R中間只有一個元素,此時這個元素就是要尋找的答案。
程式碼
二分查詢
查詢小於等於某個數的最大值
1 #include<bits/stdc++.h> 2 using namespace std; 3 int len,n[10100],want; 4 int main(){ 5 scanf("%d%d",&len,&want); 6 sort(n,n+len);//預設是升序排列的 7 int L=1,R=len,mid; 8 while(L<R) 9 { 10 mid=(L+R+1)/2; 11 if(n[mid]<=want)12 { 13 L=mid; 14 }else{ 15 R=mid-1; 16 } 17 } 18 printf("%d",L); 19 return 0; 20 }
查詢大於等於它的最小值
1 1 #include<bits/stdc++.h> 2 2 using namespace std; 3 3 const int N=1001000; 4 4 int n,m,q,a[N]; 5 5 int L,R,mid; 6 6 int main(){ 77 scanf("%d%d",&n,&m); 8 8 for(int i=1;i<=n;++i) 9 9 { 10 10 scanf("%d",&a[i]); 11 11 } 12 12 for(int i=1;i<=m;++i) 13 13 { 14 14 L=1,R=n; 15 15 scanf("%d",&q); 16 16 while(L<R) 17 17 { 18 18 mid=(L+R)/2; 19 19 if(a[mid]>=q) 20 20 { 21 21 R=mid; 22 22 }else{ 23 23 L=mid+1; 24 24 } 25 25 } 26 26 if(a[L]!=q) 27 27 { 28 28 printf("%d ",-1); 29 29 }else{ 30 30 printf("%d ",L); 31 31 } 32 32 } 33 33 return 0; 34 34 }
砍樹(P1873)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int tree=1000100; 4 int N,M,trees[tree]; 5 int L,R,mid,mx=0; 6 int check(int a){ 7 long long sum=0; 8 for(int i=1;i<=N;++i) 9 { 10 if(trees[i]>=a) 11 { 12 sum+=trees[i]-a; 13 } 14 } 15 return sum>=M; 16 } 17 int main(){ 18 scanf("%d%d",&N,&M); 19 for(int i=1;i<=N;++i) 20 { 21 scanf("%d",&trees[i]); 22 mx=max(mx,trees[i]); 23 } 24 L=0,R=mx; 25 while(L<R) 26 { 27 mid=(L+R+1)/2; 28 if(check(mid)) 29 { 30 L=mid; 31 }else{ 32 R=mid-1; 33 } 34 } 35 printf("%d",L); 36 return 0; 37 }
木材加工(P2440)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k,woods[100100]; 4 int L,R,mid,mx=0; 5 int check(int a){ 6 long long sum=0; 7 for(int i=1;i<=n;++i) 8 { 9 sum+=woods[i]/a; 10 } 11 return sum>=k; 12 } 13 int main(){ 14 scanf("%d%d",&n,&k); 15 for(int i=1;i<=n;++i) 16 { 17 scanf("%d",&woods[i]); 18 mx=max(mx,woods[i]); 19 } 20 L=0,R=mx; 21 while(L<R) 22 { 23 mid=(L+R+1)/2; 24 if(check(mid)) 25 { 26 L=mid; 27 }else{ 28 R=mid-1; 29 } 30 } 31 printf("%d",L); 32 return 0; 33 }
跳石頭(P2678)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int L,N,M,rocks[50100]; 4 int l,r,mid; 5 int check(int a){ 6 int sum=0,cu=0; 7 for(int i=1;i<=N;++i) 8 { 9 if(rocks[i]-rocks[cu]<a) 10 { 11 ++sum; 12 }else{ 13 cu=i; 14 } 15 } 16 return sum<=M; 17 } 18 int main(){ 19 scanf("%d%d%d",&L,&N,&M); 20 for(int i=1;i<=N;++i) 21 { 22 scanf("%d",&rocks[i]); 23 } 24 l=0,r=L; 25 while(l<r) 26 { 27 mid=(l+r+1)/2; 28 if(check(mid)) 29 { 30 l=mid; 31 }else{ 32 r=mid-1; 33 } 34 } 35 printf("%d",l); 36 return 0; 37 }
A-B數對(P1102)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=200100; 4 int n,c,a[N]; 5 int L,R,mid,sum=0; 6 int main(){ 7 scanf("%d%d",&n,&c); 8 for(int i=1;i<=n;++i) 9 { 10 scanf("%d",&a[i]); 11 } 12 sort(a+1,a+n+1); 13 for(int i=1;i<=n-1;++i) 14 { 15 L=i+1,R=n; 16 while(L<=R) 17 { 18 mid=(L+R)/2; 19 if(a[mid]>=a[i]+c) 20 { 21 R=mid-1; 22 }else{ 23 L=mid+1; 24 } 25 } 26 sum-=L; 27 L=i+1,R=n; 28 while(L<=R) 29 { 30 mid=(L+R)/2; 31 if(a[mid]>a[i]+c) 32 { 33 R=mid-1; 34 }else{ 35 L=mid+1; 36 } 37 } 38 sum+=L; 39 } 40 printf("%d",sum); 41 return 0; 42 }
聰明的質監員(P1314)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=200100; 4 int n,m,w[N],v[N],l[N],r[N],L,R,mid,c[N]; 5 long long s,sum[N]; 6 long long y(int a){ 7 memset(c,0,sizeof(c)); 8 memset(sum,0,sizeof(sum)); 9 for(int i=1;i<=n;++i) 10 { 11 c[i]=c[i-1]+(w[i]>=a);//這裡使用了字首和的知識 12 } 13 for(int i=1;i<=n;++i) 14 { 15 sum[i]=sum[i-1]+(w[i]>=a)*v[i]; 16 } 17 long long res=0; 18 for(int i=1;i<=m;++i) 19 { 20 res+=(c[r[i]]-c[l[i]-1])*(sum[r[i]]-sum[l[i]-1]); 21 } 22 return res; 23 } 24 int main(){ 25 scanf("%d%d%lld",&n,&m,&s); 26 for(int i=1;i<=n;++i) 27 { 28 scanf("%d%d",&w[i],&v[i]); 29 } 30 for(int i=1;i<=m;++i) 31 { 32 scanf("%d%d",&l[i],&r[i]); 33 } 34 L=0,R=1000000; 35 while(L<R) 36 { 37 mid=(L+R)/2; 38 if(y(mid)<=s) 39 { 40 R=mid; 41 }else{ 42 L=mid+1; 43 } 44 } 45 long long ans=min(s-y(L),y(L-1)-s); 46 printf("%lld\n",ans); 47 return 0; 48 }
心得
- 二分演算法雖然非常好用、高效,但做題時也要思考這道題是否需要使用二分演算法。
- 二分演算法的二分查詢部分幾乎相等,變化的一般只是檢驗部分。
- 二分演算法與一些其他的演算法/思想(如貪心、字首和等)一起使用可以實現意想不到的效果。如跳石頭(P2678)的檢驗函式:
1 int check(int a){ 2 6 int sum=0,cu=0; 3 7 for(int i=1;i<=N;++i) 4 8 { 5 9 if(rocks[i]-rocks[cu]<a) 6 10 { 7 11 ++sum; 8 12 }else{ 9 13 cu=i; 10 14 } 11 15 } 12 16 return sum<=M; 13 17 }