合并果子
Hello 大家好,我是CSDN新来的一名……小小的OI选手,刚开通了博客,比较激动233,所以先献上一次题解
那么下面切入正题(为什么不能开头空两个郁闷)
首先献上题目地址
https://www.luogu.org/problem/show?pid=1090
就是这么一道题了,目(keng)测(ding)很多大神都写过,不喜勿喷~
题目分析
很明显的贪心了,每次选两个最小的拿出来求和累加在放回去,直到只剩一堆为止
然后估计所有人一开始看到都是这样想的(手写的有语法错误请谅解哈)
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
do
{ sort(a,a+1+n,cmp);
ans+=(a[n]+a[n-1]);
a[n-1]+=a[n];
n--;
}while (n>1)
然后一看n到10000 我就怂(zhi zhang)了,于是就引入了神奇的someting
堆
大神肯定司空见惯了,如果有新手的话还是略微解释一下
一棵二叉树,保证根节点一定最优,其两个孩子次优,以此类推
因为是二叉树所以更改和删除都是logn的。
大根堆就是根节点最大,反之亦然
再回到这题,容易发现,可以把n个数建成一个小跟堆,每次用logn的时间取出2个最小值,累加到ans里面,再合并放回去
要做n-1次嘛,每次logn,故复杂度为O(nlogn),也就过了,毕竟1W还是很小的,那么下面献上程序咯
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int f[10001];
int ans,len,n;
int get()
{
return f[1];
}
void remove()
{
int son,next;
f[1]=f[len--];
son=1;
while (son*2<=len)
{
next=son*2;
if (next<len && f[next+1]<f[next]) next++;
if (f[son]<f[next])break;
swap(f[next],f[son]);
son=next;
}
}
void put(int x)
{
int son,next;
f[++len]=x;
son=len;
while (son>1)
{
next=son>>1;
if (f[son]>=f[next]) break;
swap(f[son],f[next]);
son=next;
}
}
int main()
{
len=0;
scanf("%d",&n);
int x;
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
put(x);
}
ans=0;
while (len>1)
{
int t1=get();
remove();
int t2=get();
remove();
int t3=t1+t2;
//printf("%d %d %d\n",t1,t2,t3);
put(t3);
ans+=t3;
}
printf("%d",ans);
return 0;
}
这道题也就过了,然而是手写堆,比较烦,容易错什么的,于是就有了stl库的优先队列这种interesting的东西,就是满足出队的总是最优,所以就可以写优先队列了,用上stl库,加个头文件
#include <quene>
具体用法抱歉本蒟蒻还没用用过,用过给大家下一题的时候补上
**
谢谢
**