题目链接:合并果子
分析
很明显是每次合并两个最小的。
维护一个小根堆,每次取出两个最小的,将他们的和记录并放回堆。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,tree[30001],len,ans;
void put(int x)
{
tree[++len]=x;//加到最后
int son=len;//当前位置
while(son>1)
{
int fa=son/2;
if(tree[fa]<tree[son]) break;//符合小根堆条件
else //否则继续往上移
{
swap(tree[fa],tree[son]);
son=fa;
}
}
}
int get()//不仅要取第一个,还要保证依然是小根堆
{
int t=tree[1];
tree[1]=tree[len];
len--;
int fa=1;
while(fa*2<=len)//保证有儿子
{
int son=fa*2;//先求左儿子
if(tree[son]>tree[son+1]&&son+1<=len)
{//比较两个儿子
son++;
}
if(tree[son]>tree[fa]) break;//如果比小儿子还小
else//否则小儿子作为父亲
{
swap(tree[son],tree[fa]);
fa=son;
}
}
return t;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int a;
cin>>a;
put(a);
}
for(int i=1;i<n;i++)
{
int x=get(),y=get();//每次取出最小的(贪心)
ans+=(x+y);
// cout<<ans<<' '<<x<<' '<<y<<endl;
put(x+y);//根据题意将和放进去继续准备合并
}
cout<<ans;
return 0;
}
Update on 2022.1.20
另一种建立堆的写法以及STL优先队列写法。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int n,a[100001],k,ans;
void down(int x)
{
int i=x;
while(i*2<=n)
{
i*=2;
if(a[i]>a[i+1]&&i+1<=n) i++;
if(a[i/2]>a[i]) swap(a[i/2],a[i]);
else break;
}
}
int get()
{
int t=a[1];
a[1]=a[k];
k--;
int fa=1;
while(fa*2<=k)
{
int son=fa*2;
if(a[son]>a[son+1]&&son+1<=k) son++;
if(a[son]<a[fa])
{
swap(a[son],a[fa]);
fa=son;
}
else break;
}
return t;
}
void putup(int x)
{
a[++k]=x;
int now=k;
while(now>1)
{
int fa=now/2;
if(a[fa]>a[now])
{
swap(a[fa],a[now]);
now=fa;
}
else break;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=n/2;i>=1;i--)
{
down(i);//O(n)建堆
}
k=n;
for(int i=1;i<=n-1;i++)
{
int x=get(),y=get();
ans+=x+y;
putup(x+y);
}
cout<<ans;
return 0;
}
STL:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int> q;
int n,x,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
q.push(-x);
}
for(int i=1;i<=n-1;i++)
{
int x,y;
x=q.top();q.pop();
y=q.top();q.pop();
ans+=(-x-y);
q.push(x+y);
}
cout<<ans;
return 0;
}