java合并果子_P6033合并果子加强版题解

又是被读入逼疯的一天

首先看看题目,这道题的题意不难理解,是要从这么多堆果子中选出两堆进行合并,最后要求花费力气最小,很容易就能想到是一个贪心的过程,和P1090完全一样。

因此这道题,主要就在于卡时间与变量范围。

因为当初刚过了P1090,因此当时直接把那道题的解法搬了过来,于是。。。

c2f407ea6de84fd0da20098c9e44f751.png

自此之后,再也没有更高得分。。。。

那么,首先介绍一下P1090的想法吧

优先队列

相信各位应该了解过这个东西,它是STL自带的一种队列,也叫堆,默认是对顶为最大值,而我们这道题需要最小值,因此需要声明一下

priority_queue,greater > q;

据说这个东西的效率是O(log n),因此总效率就达到了O(n*log n)。

按说这个效率其实算比较快的,但是对于这道加强版来说是不够的。

#include

#include

using namespace std;

priority_queue,greater > q;

long long n,ans=0;

const int maxn=100010;

int main()

{

scanf("%ld",&n);

for(int i=1;i<=n;i++)

{

long long m;

scanf("%ld",&m);

q.push(m);

}

for(int i=1;i

{

long long a,b;

a=q.top();

q.pop();

b=q.top();

q.pop();

ans+=(a+b);

q.push(a+b);

}

printf("%ld",ans);

return 0;

}

当时我刚学完优先队列,只能做到这个程度,然后老师告诉我们“这道题正解应该是单调队列”,于是我就开始了等待。。。

终于,有一天,当我学会了,用优先队列做这道题时。。。

优先队列

说到底,优先队列其实只是一种思想。一开始我先将所有果子按多少排序,小的在前,大的在后,然后把它们放到一个队列q1里去,此时这个队列内,前面的数肯定比后面的数小。

第一次合并,我从原队列q1里取出两个数合并,放到另一个队列q2里;

第二次,剩余果子里最小的那一堆,肯定是原队列q1的队首,或者q2的队首;

因此,每次取数时,只需要比较两个队首的大小即可。

合并过程的效率是O(n),因此时间大头就在于建立q1是的排序过程,没的说,必选桶排(因为快排不行,我试过)

所以就有了这一段代码:

#include

using namespace std;

const int maxn=10000100;//桶边界

int t1[maxn],t[maxn];

class queue

{

int fr=1,t=1;

unsigned long long q[maxn];

public:

void push(unsigned long long a)

{

q[t++]=a;

}

unsigned long long size()

{

return t-fr;

}

bool empty()

{

if(t>fr)return 0;

else return 1;

}

void pop()

{

fr++;

}

unsigned long long front()

{

return q[fr];

}

}q1,q2;

int main()

{

int n;

long long ans=0;

bool flag=false;

//memset(t1,0,sizeof(t1));

scanf("%d",&n);

long long m=0;

for(int i=1;i<=n;i++)

{

long long a;

scanf("%d",&a);

t1[a]++;

if(m

}

long long id=0;

for(int i=1;i<=m;i++)

while(t1[i]--)q1.push(i);

long long a,b,fr=1;

while((q1.size() + q2.size() )>1)

{

if((q1.front()>q2.front() || q1.empty()) && q2.size()) a=q2.front(),q2.pop();

else a=q1.front(),q1.pop();

if((q1.front()>q2.front() || q1.empty()) && q2.size()) b=q2.front(),q2.pop();

else b=q1.front(),q1.pop();

ans+=(a+b);

//cout<

q2.push(a+b);

}

printf("%ld",ans);

return 0;

}

(不知道luogu对我可能有什么意见,queue < long long> q; 定义出来的队列用不了,我就只能自己写一个队列)

然而,当我信心满满地以为我能成功的时候,现实给了我一盆冷水

2e5ef50d823db33b13b2c89e5e9fd6a6.png

脑中飘过一万头cnm

我只能说我也很无奈,我只感觉受到了教练的欺骗(误)

当然,只要思想不滑坡,方法总比困难多

在我看了看其他大佬们的题解后,我发现我与正确只差了一个快读

于是:

//未完待续

#include

using namespace std;

const int maxn=10000100;//桶边界

int t1[maxn],t[maxn];

class queue

{

int fr=1,t=1;

unsigned long long q[maxn];

public:

void push(unsigned long long a)

{

q[t++]=a;

}

unsigned long long size()

{

return t-fr;

}

bool empty()

{

if(t>fr)return 0;

else return 1;

}

void pop()

{

fr++;

}

unsigned long long front()

{

return q[fr];

}

}q1,q2;

void read(int &x){

int f=1;x=0;char s=getchar();

while(s'9'){if(s=='-')f=-1;s=getchar();}

while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}

x*=f;

}

int main()

{

int n;

long long ans=0;

bool flag=false;

memset(t1,0,sizeof(t1));

read(n);

for (int i = 1; i <= n; ++i) {

int a;

read(a);

t1[a] ++;

}

long long id=0;

for(int i=1;i<=100000;i++)

while(t1[i]--)q1.push(i);

long long a,b,fr=1;

while((q1.size() + q2.size() )>1)

{

if((q1.front()>q2.front() || q1.empty()) && q2.size()) a=q2.front(),q2.pop();

else a=q1.front(),q1.pop();

if((q1.front()>q2.front() || q1.empty()) && q2.size()) b=q2.front(),q2.pop();

else b=q1.front(),q1.pop();

ans+=(a+b);

q2.push(a+b);

}

printf("%ld",ans);

return 0;

}

0124677ed9cc7695fe6174e2a2865501.png

亲测AC有效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值