【暑假集訓筆記】二分查詢(二分查詢數值+二分64種類型+最大化最小值+最小化最大值+最大化平均值模型)
阿新 • • 發佈:2019-02-09
//二分查詢總結:由於本人二分常年寫成死迴圈 /* 簡介:二分查詢 == 折半查詢 要求:線性表,有序表(注意升序與降序) 思想: 設查詢區間[L,R] 取中點 mid = (L+R)/2 判定mid是否符合要求:(如何判斷:bool check(int mid); ) 是:縮短區間求邊界;直接返回; 否:縮短區間 最終結果val = R 或者val = L; */ //典型線性表查詢資料 int er_search(int a[],int n, int key) { const int inf = 0x3f3f3f3f; int L=0,R=n; while(L<R){ int mid = (L+R)/2; if(key==a[mid]){ return mid; } else if(key<a[mid]){ R=mid-1; } else if(k>a[mid]){ L=mid+1; } } return inf; } //CSDN某部落格對二分的64種分類 /* *取整方式 向下取整(最小值) 向上取整(最大值) *區間開閉 閉區間 左閉右開區間 左開右閉區間 開區間 *問題型別 單增 對於不下降序列a,求最小的i,使得a[i] = key 對於不下降序列a,求最大的i,使得a[i] = key 對於不下降序列a,求最小的i,使得a[i] > key 對於不下降序列a,求最大的i,使得a[i] < key 單減 對於不上升序列a,求最小的i,使得a[i] = key 對於不上升序列a,求最大的i,使得a[i] = key 對於不上升序列a,求最小的i,使得a[i] < key 對於不上升序列a,求最大的i,使得a[i] > key */ //下面四個不下降的例子 //a[] = 1 2 3 4 5 6 6 6 7 9 //min i,a[i] = key; =>a[5] while(s < e){ mid = (e+s)/2;// 向下取整 if(key <= a[mid]) e = mid; else s = mid + 1; } //max i,a[i] = key =>a[7] while(s < e){ mid = (e+s+1)/2;// 向上取整 if(key >= a[mid]) s = mid; else e = mid - 1; } //min i, a[i] > key =>=>a[8] while(s < e){ mid = (e+s)/2;//向下取整 if(key < a[mid]) e = mid; else s = mid + 1; } // max i, a[i] < key =>a[4] while(s < e){ mid = (e+s+1)/2;//向上取整 if(key > a[mid]) s = mid; else e = mid - 1; } /*巧記,但不是完全正確 迴圈:L<R 求mid時:求max :L+R+1 求min: L+R; if():真實值與猜測值的關係作為條件:max-真實大於猜測 min-真實小於猜測 防死迴圈:調整if下的L或者R 另一個邊界在else下注意+-1; 總結:迴圈L<R mid注意1 else下防死迴圈 */ //另一種簡單分類:第一個大於v,第一個大於等於v,最後一個小於v,最後一個小於等於v /*內容來自:http://www.cnblogs.com/xiaowuga/p/8604750.html 第一個大於等於v: lower_bound(ForwardIterator first, ForwardIterator last,const T& val, Compare comp) 我們假設L為當前區間的答案,R為當前區間的實際答案(因為R是第一個大於等於v的下標), 我們每次二分的實際上是為了讓L和R不斷靠近, 所以當L==R的時候,我們假設的答案等於實際的答案,那麼就結束迴圈了,返回答案L。 while(L<R){ int M=(L+R)/2; if(a[M]>=v) R=M;//R是第一個大於等於v下標,那麼R最大隻能是m else L=M+1;//[M,R)區間內的下標都是小於v的,L作為最後的答案最小隻能是M+1 } 第一個大於v: upper_bound(ForwardIterator first, ForwardIterator last,const T& val, Compare comp); 我們假設L為當前區間的答案,R為當前區間的實際答案(因為R是第一個大於v的下標), 我們每次二分的實際上是為了讓L和R不斷靠近, 所以當L==R的時候,我們假設的答案等於實際的答案,那麼就結束迴圈了,返回答案L。 while(L<R){ int M=(L+R)/2; if(a[M]>v) R=M;//R是第一個大於v下標,那麼R最大隻能是M else L=M+1;//[M,R)區間內的下標都是小於等於v的,L作為最後的答案最小隻能是M+1 } 最後一個小於等於v 我們假設R為當前區間的答案,L為當前區間的實際答案(因為L是最後一個小於等於v的下標), 我們每次二分的實際上是為了讓L和R不斷靠近, 所以當L==R的時候,我們假設的答案等於實際的答案,那麼就結束迴圈了,返回答案L。 while(L<R){ int M=(L+R+1)/2; if(a[M]<=v) L=M;//L是最後一個小於等於v下標,那麼L最小隻能是M。 else R=M-1;//(L,M]區間內的下標都是大於v的,R作為最後的答案最大隻能是M-1。 } 最後一個小於v 我們假設R為當前區間的答案,L為當前區間的實際答案(因為L是最後一個小於v的下標), 我們每次二分的實際上是為了讓L和R不斷靠近, 所以當L==R的時候,我們假設的答案等於實際的答案,那麼就結束迴圈了,返回答案L。 while(L<R){ int M=(L+R+1)/2; if(a[M]<v) L=M;//L是最後一個小於v下標,那麼L最小隻能是M。 else R=M-1;//(L,M]區間內的下標都是大於等於v的,R作為最後的答案最大隻能是M-1。 } */ //二分條件搜尋的應用:最大化最小值,最小化最大值,最大化平均值 /*最大化最小值 邊界屬性: 正確答案ans 滿足條件但不是最大:ans-1 不滿足條件:ans+1 二分型別:最後一個滿足條件的ans值 思路: 把條件看成具體的值 求最後一個小於等於v */ /* 最小化最大值 邊界屬性: 正確答案ans 滿足條件但不是最小:ans+1 不滿足條件:ans-1 二分型別:第一個滿足條件的值 思路: 把條件看成具體的值 求第一個大於等於v */ /*最大化平均值: 模型構建: 有n個物品,每個物品分別對應一個重量w和價值v。要求選出k個,使得平均每單位重量的價值最大。 二分模型:搜尋滿足條件check(mid)的最大解 check()模型: 構建過程: 單個物品: 平均價值:v/w 判斷這個物品是否被挑選(是否滿足單位價值mid) v/w>=mid 化簡得 v-wx>=0; 可記錄c[i] = v-wx(第i個) 多個物品: 迴圈所有物品 k個物品 求和c[0]--c[k-1] 如果這個和大於零,說明總體的平均值大於mid 模型:二分查詢最大化平均值 int n;資料組數 int k;要求件數 bool check(double mid){ c[i] = v[i]-mid*w[i]; sort(c,c+n,cmp);//逆序 double sum=0; sum+=c[0] ---c[k-1] return sum>=0;//sum = 0時候是正好滿足條件 } */