题目:传送门
Solution:
二分答案和贪心检验就是这道题思路的核心,我来详细介绍一下其中的过程,直接算可以制造冰激凌的数量的话很困难,我感觉主要是有一个k的存在使得直接计算不好去想(我是不会的。。),但是检验的话就很好操作了,我们可以用二分的方法去一步一步缩小答案区间,最后就可以找到最大值了。
那么该如何检验呢?为了使得数量最大,我们每次肯定挑最小的那个放在顶层,然后找它的下面一层。举个例子,如果我们要验证是否能制作x个,我们就先挑出最小的x个来作为顶层,然后从x + 1开始遍历一遍依次放在这x个的下一层,直到最后。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 3 * 1e5 + 100;
ll cake[maxn], a[maxn];
int t, n, k;
bool check(int x) //贪心检验
{
if(!x)
return true;
int j = x + 1;
for(int i = 1; i <= n; ++i)
cake[i] = a[i];
int i;
for(i = x + 1; i <= n; ++ i)
{
while(i <= n && cake[j - x] * 2 > a[i])
i++;
if(i > n)
return false;
cake[j++] = a[i];
if((j - 1) / x >= k) return true;
}
if((j - 1) / x >= k)
return true;
else
return false;
}
int Research(int l, int r) //二分答案
{
int res = -1;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) {
res = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
return res;
}
int main()
{
//freopen("in.txt", "r", stdin);
int s = 0;
cin >> t;
while(t --) {
cin >> n >> k;
for(int i = 1; i <= n; ++ i) {
scanf("%lld", &a[i]);
}
sort(a + 1, a + 1 + n);
int ans = Research(0, n / k);
printf("Case #%d: ", ++ s);
cout << ans << endl;
}
return 0;
}