2016 EC-Final Problem D. Ice Cream Tower(二分+贪心)

题目链接

题意:这是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时)

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/83243360