題目連接:php
http://codeforces.com/gym/101194/attachmentsc++
題意:code
給出 $N$ 個冰淇淋球,第 $i$ 個冰淇淋球大小爲 $B_i$,如今已知冰淇淋球堆疊起來可組成一個冰淇淋。blog
對於上下相鄰的兩個冰淇淋球,只有上面的那個大小不超過下面的那個的通常,才能堆疊起來。隊列
如今已知 $K$ 個冰淇淋球能夠組成一個冰淇淋,問給出的 $N$ 個冰淇淋球最多能組成多少個冰淇淋。ci
假的題解:get
比賽的時候想的一個(假的)貪心思路:it
從最大的冰淇淋球開始貪心,對於目前這個球,始終選擇小於等於當前球體積的一半中體積最大的冰淇淋球。io
進而可得這樣一個用隊列維護 $O(N)$ 的作法:
從大到小枚舉冰淇淋球,用一個隊列維護:目前產生的冰淇淋的最上端的那個冰淇淋球,以及該冰淇淋包含的冰淇淋球數目。
枚舉到當前這個球 $B_i$,與隊首冰淇淋進行比較,若能夠放到這個冰淇淋上,就放上去產生一個新的冰淇淋,出隊隊首元素,入隊一個新冰淇淋;不然就做爲一個新的冰淇淋入隊。
代碼(AC on UVALive 7900, WA on Gym 101194D):
#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); } }