有n个冰淇淋球,他们的大小一一给出,现在要做冰淇淋,一个冰淇淋必须满足上面的冰淇淋球至少是下面的冰淇淋球的两倍大。现在问你,最多能做多少个由m个冰淇淋球组成的冰淇淋。
这种给你所有原料,问你最多能组成多少某物品的题,二分还是比较明显的。但是这题的恶心之处在于check。其实也不难,我们先将冰淇淋球按大小排序,然后开始二分答案。当我们二分出答案是mid,那么我们先将mid个最小的冰淇淋球摆成一排,然后循环m-1次(因为已经摆好一层了),直到找到一个是当前位置两倍大的冰淇淋,更新这个位置。如果最后可以用<=n个冰淇淋球做完mid个冰淇淋,那么mid满足条件,反之不满足。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <list>
#include <set>
#include <queue>
#include <string>
#include <cstring>
using namespace std;
#define ll long long
#define lowbit(a) (a & (-a))
#define endl "\n"
#define PI acos(-1.0)
#define mod 1000000009
#define INF 0x3f3f3f3f
#define MAX 200010
ll p[300010];
ll check_[300010];
ll n, m;
bool check(ll x)
{
for (ll i = 1; i <= x; i++)
check_[i] = p[i];
ll cot = 1 + x;
for (ll j = 1; j < m; j++)
{
for (ll i = 1; i <= x; i++)
{
while (check_[i] * 2 > p[cot] && cot <= n)
cot++;
if (cot >= n + 1)
return 0;
check_[i] = p[cot++];
}
}
return 1;
}
int main()
{
ll t;
cin >> t;
int cas = 1;
while (t--)
{
scanf("%lld %lld", &n, &m);
for (ll i = 1; i <= n; i++)
scanf("%lld", &p[i]);
sort(p + 1, p + 1 + n);
ll l, r, mid, ans = 0;
l = 1;
r = n / m;
while (l <= r)
{
mid = (l + r) / 2;
if (check(mid))
{
l = mid + 1;
ans = mid;
}
else
r = mid - 1;
}
printf("Case #%d: %lld\n", cas++, ans);
}
return 0;
}