又是被读入逼疯的一天
首先看看题目,这道题的题意不难理解,是要从这么多堆果子中选出两堆进行合并,最后要求花费力气最小,很容易就能想到是一个贪心的过程,和P1090完全一样。
因此这道题,主要就在于卡时间与变量范围。
因为当初刚过了P1090,因此当时直接把那道题的解法搬了过来,于是。。。
自此之后,再也没有更高得分。。。。
那么,首先介绍一下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; 定义出来的队列用不了,我就只能自己写一个队列)
然而,当我信心满满地以为我能成功的时候,现实给了我一盆冷水
脑中飘过一万头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;
}
亲测AC有效