题意:这是2016 ACM-ICPC China-Final的D题,一共有N个冰淇淋球,做一个冰淇淋需要K个球,并且由于稳定性,这K个球还必须满足上下相邻的下面比上面大至少两倍。先给出N个球的质量,问最多能做出多少个冰淇淋?
分析:可以这样来分析,先将所有的数据a数组从小到大排一下序,然后再枚举所有的情况,每次二分能够组成的塔的个数mid,然后将排完序的数列的前mid个取到另一个b数组中,分别当作这mid个冰淇凌塔的最上面的那一个,然后进而贪心枚举,如果遇到比当前b数组的质量大于二倍的那就将其赋值到当前的b[i-v]的下面(这里的b数组是经过空间优化的,就是比如当前的mid=3,b[1],b[2],b[3]分别是这三个塔的最上面的,那么b[4],b[5],b[6]就分别是b[1],b[2],b[3]下面的冰激凌,这一点要明确),如果能完全枚举这n个(只要不超过n冰激凌),就是能完成mid冰激凌塔的制作。。。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <set>
#include <utility>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
const int N = 3e5 + 10;
const int MOD = 1e9+7;
ll a[N];
ll b[N];
int k,n;
bool solve(int v)
{
rep(i,1,v)
b[i]=a[i];
int p=v+1;
rep(i,v+1,v*k)
{
while(a[p]<b[i-v]*2&&p<=n) p++;
if(p==n+1) return 0;
b[i]=a[p];
p++;
}
return 1;
}
int main()
{
// freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
rep(o,1,t){
scanf("%d%d",&n,&k);
rep(i,1,n)
scanf("%lld",&a[i]);
sort(a+1,a+1+n);
// int l=0,r=n+1;
// int ans=0;
// while(r-l>1){
// int mid=(l+r)/2;
// if(solve(mid))
// l=mid;
// else
// r=mid;
// }
// printf("Case #%d: %d\n",o,l);
int l=0,r=n;
int ans=0;
while(l<=r){
int mid=(l+r)/2;
if(solve(mid))
ans=mid,l=mid+1;
else
r=mid-1;
}
printf("Case #%d: %d\n",o,ans);
}
return 0;
}
上面的两种方法都是可以实现二分的,但是要注意谁是闭区间的端点,是左边还是右边,这取决于return 的结果,但是最终的答案一定是闭区间的端点值;
上面注释的二分方法要注意右区间的取值是>=n,一开始写的是r=n,结果w了,因为这样是怎么都枚举不到n的(当k=1时)