前言:二分查找一般用於暴力枚舉下的優化,在有序的條件下,能極大提升查找效率。
分巧克力
兒童節那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友們。
小明一共有N塊巧克力,其中第i塊是Hi x Wi的方格組成的長方形。
爲了公平起見,小明需要從這 N 塊巧克力中切出K塊巧克力分給小朋友們。切出的巧克力需要滿足:
1. 形狀是正方形,邊長是整數
2. 大小相同
例如一塊6x5的巧克力可以切出6塊2x2的巧克力或者2塊3x3的巧克力。
當然小朋友們都希望得到的巧克力儘可能大,你能幫小Hi計算出最大的邊長是多少麼?
輸入
第一行包含兩個整數N和K。(1 <= N, K <= 100000)
以下N行每行包含兩個整數Hi和Wi。(1 <= Hi, Wi <= 100000)
輸入保證每位小朋友至少能獲得一塊1x1的巧克力。
輸出
輸出切出的正方形巧克力最大可能的邊長。
樣例輸入:
2 10
6 5
5 6
樣例輸出:
2
爲了更好地瞭解二分查找,我們這裏將暴力枚舉與二分查找進行對比。
舉例比較暴力枚舉與二分查找
在已排序的數組中,假設我們找到一個數——10。
暴力枚舉:從頭開始找。複雜度爲O(n)
二分查找:從中間開始找。複雜度爲O(log2n)
假設n=1010時,要找的數在數組結尾。
暴力枚舉時間複雜度:O(1010)。
二分查找時間複雜度:O(log21e10)≈O(33)。
查找效率得到極大提升。
暴力枚舉:記錄每塊巧克力大小,將可切割尺寸從大到小枚舉,輸出第一個符合條件的尺寸值。
複雜度:O(n2) 題目最大n=100000,不能在規定時間內通過評測
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll N, K;
ll H[100010], W[100010];
int main()
{
cin>>N>>K;
int _max=0, _min=0;
for(int i=0; i<N; i++)
{
cin>>H[i]>>W[i];
H[i]>W[i]?_min=W[i]:_min=H[i];
if(_max<=_min) _max=_min;
}
int cnt;
for(int i=_max; i>=0; i--)
{
cnt = 0;
for(int j=0; j<N; j++)
cnt += H[j]/i * W[j]/i;
if(cnt >= K)
{
cout<<i;
break;
}
}
return 0;
}
二分查找:不同於暴力枚舉的地方在於二分查找優化枚舉環節
從中間巧克力尺寸,開始查找。若滿足條件則向上查找,否則向下查找,直到邊界指針(r、l)相交。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll N, K;
ll H[100010], W[100010];
int main()
{
cin>>N>>K;
for(int i=0; i<N; i++)
{
cin>>H[i]>>W[i];
}
//二分查找
int mid, cnt, flag, ans = 0;
int r = 100001;
int l = 1;
while(l<=r)
{
cnt = 0, flag = 0;
mid = (r + l) / 2;
for(int i=0; i<N; i++)
{
cnt += H[i]/mid * W[i]/mid;
if(cnt >= K)
{
flag=1;
ans = mid;
l = mid + 1;
break;
}
}
if(flag==0)
r = mid - 1;
}
cout<<ans;
return 0;
}
希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。