之前學習的二分,現在感覺突然理解許多,補一下總結
首先,二分能夠解決什么樣的問題呢,個人認為,二分能夠快速解決已經知道答案范圍(線性)但是不知道確切答案的問題,例如在一個有序序列中查找某一元素出現的(最早,最晚)位置,求某單調(或在給定區間上單調)函數的零點,最大化最小值或者最小化最大值等等
二分的模板大體如下:
int l=x;//x表示元素可能出現的最小(最左邊)的情況
int r=y;//y表示元素可能出現的最大(最右邊)的情況
int ans,mid;
while(i<=l)
{mid=(l+r)/2; //二分if(check(mid))//判斷中點的情況{l=mid+1;ans=mid; //或者ans=max(ans,mid)等,ans用于保存答案,如果需要所有可行答案中的最大值或者最小值加上max或者min即可}else{r=mid-1;}
}
總體來講二分的思想還是比較簡單的,但是問題的難點經常不是考慮不到二分,而是考慮到二分卻不知道如何判斷要查找的元素在mid的情況下能否可行,即如何編寫check(mid)函數。這個函數的編寫往往要仔細考慮要查找元素的特征(以及腦洞)。
比如經常出現的最大化最小值和最小化最大值等,那些放牛的等問題判斷起來還是比較容易的,就從開始一個一個按照條件放,如果能行就能行。但是更多的問題還是比較靈活的,比如POJ - 3104
題意大概是一個人冬天洗衣服想要讓衣服盡快的干問最少需要多少時間。
這個我們很容易確定所花最長時間是水分最多的衣服所含有的水分(不用那個烘干器自然風干),最短時間為0,但是給定一個時間,我們怎么判斷這個時間內可不可以將衣服晾干呢?
這就需要我們進行一個轉換,我們不妨這樣想:這些衣服先晾了mid分鐘,然后再將還沒有干的全部用烘干器烘干,烘干器只能烘mid次,每次烘干k-1的水分。
乍看好像有些無厘頭,其實仔細一想很簡單,我們只不過把原來一起做的事情拆開來分析而已(表示不好想到)。
這樣的話我們遍歷每件衣服,如果它沒有自己晾干就得用烘干器,如果在mid次以內全部烘干了就可以,如果沒有就不行。
需要注意一點的是如果原本烘干器是個假的烘干器(每分鐘烘干的速度和晾衣服的速度一樣),那么只處理晾后的,就不要用烘干器再處理了(烘干器每次處理0,不注意這點的話可能會re,我re了好多次才注意到)。
附AC代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int n,k;
int a[100100];bool cmp(int a,int b)
{return a>b;
}
bool check(int time)
{int tmp=time;int t;for(int i=0;i<n;i++){t=a[i]-time;if(t>0){if(k==0)return false;tmp-=t/k;if(t%k!=0)tmp--;}if(tmp<0)return false;}return true;
}
int main()
{int tmp;memset(a,0,sizeof(a));scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&a[i]);}scanf("%d",&k);k--;sort(a,a+n,cmp);int l=0,r=a[0];int ans=0,mid;while(l<=r){mid=(l+r)/2;if(check(mid)){r=mid-1;ans=mid;}else{l=mid+1;}}printf("%d",ans);return 0;
}
除此之外二分法快速冪也經常使用,必須熟記(雖然我覺得是一個遞歸)
快速冪板子(非遞歸寫法):
long long qucik_pow(long long a,long long n,long long m)//求a^n%m
{long long ans=1;while(n){if(n&1) ans=(ans*a)%m;a=a*a%m;n>>=1;}return ans%m;
}
還有遞歸寫法(有助于理解快速冪,但是不常用)
long long quick_pow(long long a,long long n,long long m)
{if(n==1) return a;if(n%2==0){long long t=quick_pow(a,n/2,m);return t*t%m;}else{long long t=quick_pow(a,b/2,m);t=t*t%m;t=t*a%m;return t;}
}
三分的話每太用過,只是見過用來求凸凹函數的極值點
思想就是先將區間二分,在將其中一個區間二分,然后比較兩個點的大小,哪個距離極值點遠就將哪一邊更新,應該是可以根據凹凸函數的性質進行證明的(我比較懶就不證明了,有時間再證吧),附一下板子:
while(left+eps<right)
{midl=(left+right)/2;midr=(midl+right)/2;if(f(midl)<f(midr)){right=midr;}else{left=midl;}
}