【Codeforces-140C】New Year Snowmen(貪心+優先佇列、二分)
As meticulous Gerald sets the table and caring Alexander sends the postcards, Sergey makes snowmen. Each showman should consist of three snowballs: a big one, a medium one and a small one. Sergey's twins help him: they've already made n snowballs with radii equal to r1, r2, ..., r
Input
The first line contains integer n (1 ≤ n ≤ 105) — the number of snowballs. The next line contains n integers — the balls' radii r1, r2, ..., rn (1 ≤ ri ≤ 109). The balls' radii can coincide.
Output
Print on the first line a single number k — the maximum number of the snowmen. Next k
Examples
Input
7 1 2 3 4 5 6 7
Output
2 3 2 1 6 5 4
Input
3 2 2 3
Output
0
思路:
一開始的想法是從前往後貪心,但是後來發現從前往後貪心和從後往前貪心都是不對的,比如後面大的元素很多,但從前往後把小的都貪心完了(小的和小的組合和),導致大的沒法用,其實可以選出一部分小的和大的組合。同理從後往前貪心也一樣。
因此這道題是取數量比較多的前三個,這三個組合。每次只能取一個,因為取完一次後,它們再放回佇列後可能就不是最大的了,因此,不能一次就全取完。
這道題,還是想的不夠,開始想的是從前往後直接掃,後來發現不行,再後來想到找最大的,可是因為一次就把三個元素都組合完了(其實每次應該只組合出一個雪人,但是我組合了多個)。還要加強思維的鍛鍊。
ac程式碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<string.h>
#define ll long long
using namespace std;
struct node{
ll x,y,z;
}ans[101010];
int n;
map<ll,int> mp;
map<ll,int>::iterator it,it1,it2;
struct cmp{
bool operator() (const pair<ll,int> p1,const pair<ll,int> p2){
return p1.second < p2.second; /// first 小的先彈出
}
};
priority_queue<pair<ll,int>,vector<pair<ll,int> > ,cmp > que;
int main()
{
cin>>n;
ll a;
for(int i=0;i<n;i++)
{
scanf("%lld",&a);
mp[a]++;
}
pair<ll,int> p1;
for(it=mp.begin();it!=mp.end();it++)
{
p1.first=it->first;
p1.second=it->second;
que.push(p1);
}
int cnt=0,sum=0;
while(1)
{
if(que.size()<3)
break;
pair<ll,int> t1=que.top();
que.pop();
pair<ll,int> t2=que.top();
que.pop();
pair<ll,int> t3=que.top();
que.pop();
sum++;
if(t1.first>t2.first)
{
if(t1.first>t3.first)
{
if(t2.first>t3.first)
{
ans[cnt].x=t1.first;
ans[cnt].y=t2.first;
ans[cnt++].z=t3.first;
}
else
{
ans[cnt].x=t1.first;
ans[cnt].y=t3.first;
ans[cnt++].z=t2.first;
}
}
else
{
ans[cnt].x=t3.first;
ans[cnt].y=t1.first;
ans[cnt++].z=t2.first;
}
}
else
{
if(t1.first<t3.first)
{
if(t2.first>t3.first)
{
ans[cnt].x=t2.first;
ans[cnt].y=t3.first;
ans[cnt++].z=t1.first;
}
else
{
ans[cnt].x=t3.first;
ans[cnt].y=t2.first;
ans[cnt++].z=t1.first;
}
}
else
{
ans[cnt].x=t2.first;
ans[cnt].y=t1.first;
ans[cnt++].z=t3.first;
}
}
t1.second--;
t2.second--;
t3.second--;
if(t1.second!=0)
{
que.push(t1);
}
if(t2.second!=0)
{
que.push(t2);
}
if(t3.second!=0)
{
que.push(t3);
}
}
printf("%d\n",sum);
for(int i=0;i<cnt;i++)
{
printf("%lld %lld %lld\n",ans[i].x,ans[i].y,ans[i].z);
}
return 0;
}
看了cf大佬們的程式碼發現還可以用二分做:(大佬的程式碼,按照自己的理解加了點註釋)
做法:二分找可能值,判斷符不符合條件,然後判斷下次二分的區間。
%%%%%
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<set>
#define N 120000
using namespace std;
int f[N][3],a[N],l,r,mid,n,i;
bool check(int mid){
int i,v,j;
for(i=1;i<=mid;++i)f[i][0]=a[i];
/*
因為這次是看能不能放mid個,因此先把最上面一層放上前mid個(小的),
因為如果不這樣放而是放了比較大的,那麼下面在放的時候可能就無法放了(大的雪球不夠了),
而放小的可以避免這個,並且取得是最優的解。
小的雪球如果放在中間一層,那麼最上面一層就需要在放比較大的,那麼中間和下面放的就更大,要求更高。
*/
j=1; v=0;
for(i=mid+1;i<=n;++i){
if(a[i]>f[j][v]){ //這一層要放的比上一層的要大
f[j][v+1]=a[i];//放第v+1層
j++;
if(j>mid)j=1,v++;//第二層已經放了mid個就是這次放滿了。
if(v==2)return 1;//最後一層已經擺滿
}
}
return 0;
}
int main(){
scanf("%d",&n);
for(i=1;i<=n;++i)scanf("%d",&a[i]);
sort(a+1,a+n+1);
l=1; r=n/3;//因為三個組成一個雪人,總共有n個因此,最大值是n/3.
while(l<=r){
mid=(l+r)/2;
if(check(mid))l=mid+1;//mid這個點是一定符合條件的,而我們要找最大的,因此區間往右縮。而且l-1這個點是一定符合條件的
else r=mid-1;//mid這個點不符合條件。-1為了防止死迴圈
}
check(l-1);
printf("%d\n",l-1);
for(i=1;i<=l-1;++i)printf("%d %d %d\n",f[i][2],f[i][1],f[i][0]);
}