1. 程式人生 > >Gym 101194D / UVALive 7900 - Ice Cream Tower - [二分+貪心][2016 EC-Final Problem D]

Gym 101194D / UVALive 7900 - Ice Cream Tower - [二分+貪心][2016 EC-Final Problem D]

題目連結:

http://codeforces.com/gym/101194/attachments

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5922

 

題意:

給出 $N$ 個冰淇淋球,第 $i$ 個冰淇淋球大小為 $B_i$,現在已知冰淇淋球堆疊起來可組成一個冰淇淋。

對於上下相鄰的兩個冰淇淋球,只有上面的那個大小不超過下面的那個的一般,才能堆疊起來。

現在已知 $K$ 個冰淇淋球可以組成一個冰淇淋,問給出的 $N$ 個冰淇淋球最多能組成多少個冰淇淋。

 

假的題解:

比賽的時候想的一個(假的)貪心思路:

  從最大的冰淇淋球開始貪心,對於目前這個球,始終選擇小於等於當前球體積的一半中體積最大的冰淇淋球。

進而可得這樣一個用佇列維護 $O(N)$ 的做法:

  從大到小列舉冰淇淋球,用一個佇列維護:目前產生的冰淇淋的最上端的那個冰淇淋球,以及該冰淇淋包含的冰淇淋球數目。

  列舉到當前這個球 $B_i$,與隊首冰淇淋進行比較,若可以放到這個冰淇淋上,就放上去產生一個新的冰淇淋,出隊隊首元素,入隊一個新冰淇淋;否則就作為一個新的冰淇淋入隊。

 

程式碼(AC on UVALive 7900, WA on UVALive 7900):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int maxn=3e5+10;

int n,k;
ll b[maxn];
queue<pli> Q;

int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++)
    {
        cin>>n>>k;
        for
(int i=1;i<=n;i++) scanf("%lld",&b[i]); sort(b+1,b+n+1); int ans=0; while(!Q.empty()) Q.pop(); for(int i=n;i>=1;i--) { if(Q.empty()) { Q.push(make_pair(b[i],1)); continue; } pli now=Q.front(); if(b[i]<=(now.first>>1)) { Q.pop(); if(now.second+1>=k) ans++; else Q.push(make_pair(b[i],now.second+1)); } else Q.push(make_pair(b[i],1)); } printf("Case #%d: %d\n",kase,ans); } }

 

 

真的題解:

若要求做 $cnt$ 個冰淇淋,那麼肯定先取最小的 $cnt$ 個冰淇淋球作為頂,然後一點點往前後推判斷是否真的能做出 $cnt$ 個球。

那麼,就可以二分答案,最少 $0$ 個冰淇淋,最多 $\left \lfloor \frac{N}{K} \right \rfloor$ 個冰淇淋。

時間複雜度 $O(N \log \left \lfloor \frac{N}{K} \right \rfloor)$

 

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pli;
const int maxn=3e5+10;

int n,k;
ll b[maxn];
queue<pli> Q;
inline bool judge(int cnt)
{
    for(int i=1;i<=cnt;i++) Q.push(make_pair(b[i],1));
    for(int i=cnt+1;i<=n;i++)
    {
        pli now=Q.front();
        if(b[i]>=(now.first<<1))
        {
            Q.pop();
            Q.push(make_pair(b[i],now.second+1));
        }
    }
    int res=0;
    while(!Q.empty())
    {
        res+=(Q.front().second>=k);
        Q.pop();
    }
    return res>=cnt;
}

int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++)
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
        sort(b+1,b+n+1);
        int l=0, r=n/k;
        while(l<r)
        {
            int mid=(l+r+1)>>1;
            if(judge(mid)) l=mid;
            else r=mid-1;
        }
        printf("Case #%d: %d\n",kase,l);
    }
}