藍橋杯17屆第九題——分巧克力(二分查找)

前言:二分查找一般用於暴力枚舉下的優化,在有序的條件下,能極大提升查找效率。

分巧克力

兒童節那天有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;
}

希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章