题目描述
输入
输出
样例输入
样例输出
提示
这题显然是用贪心,每次找的最小的两堆,合并,再找最小的两堆。这题如果纯模拟的话复杂度为O(n^2*logn) ,显然会超,这时我们会用到一个叫堆的数据结构,(此题中就是哈夫曼树),将n堆果子初始化为小根堆,每次找出最小的两个并维护小根堆的性质即可,复杂度降为O(nlogn)。
#include<cmath>
#include<vector>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<memory.h>
#include<iostream>
#include<algorithm>
#define MAXN 10100
using namespace std;
int n;
long long Ans = 0;
int heap[MAXN],size = 0;
void Adjust(int k,int len){
int left_node = 2*k, right_node = 2*k+1, root = k;
if (root <= len/2){
if (left_node <= len && heap[left_node] < heap[root]) root = left_node;
if (right_node <= len && heap[right_node] < heap[root]) root = right_node;
//找出当前节点最小的,如果根节点就是最小的,不必再往下找
if (root != k) {
swap(heap[k],heap[root]);
Adjust(root,len);//维护以交换的结点为根的子堆
}
}
}
void HeapWork(int len){
int Root;
for (int i=len;i>=2;i--) {
Root = heap[1];//取出跟(最小的一堆)
heap[1] = heap[i];
Adjust(1,i-1);//重新调整,找到第二小的
heap[1] += Root;//将最小的两堆合并,加入堆
Ans += heap[1];
Adjust(1,i-1);//维护小根堆
}
}
int main(){
cin >> n;
for (int i=1;i<=n;i++) scanf("%d",&heap[i]);
sort(heap+1,heap+n+1);
//初始化:读入后先排序预处理,建立小根堆。
HeapWork(n);//合并果子
cout << Ans ;
return 0;
}
这题贪心证明如下:
首先,假设合并之后只是把两堆搬到一起,不合并
那么,合并x,y堆所消耗的体力就是组成x的堆的果子数+组成y堆的果子数
这样,假设某一堆t,被某一个堆合并,又被某一个堆合并...重复m次
那么单单考虑这个t堆,它所需要消耗的体力将会是m*果子数(t)
这样的话,合并的总代价相当于:m(1)*果子数(1) +m(2)*果子数(2) +...+m(n)*果子数(n)
而果子数是一定的,m(1)+m(2)+...+m(n)的总和是一定的(因为总合并次数一定),所以我们只需要使果子数大的那一堆的m尽量小,使果子数小的那一堆的m尽量大
一种简单的策略就是每次取两个最小的堆,合并。
这样可以使果子数小的堆多次合成,果子数大的堆少合成
就取到了最优值。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
我发现某大神更精练的写法。。。
#include <iostream>
#include <cstring>
using namespace std;
int heap[30001],size=0;
void put(int n)
{
heap[++size]=n;
int now=size;
while(now>1){
int next=now/2;
if(heap[now]>=heap[next])return;
swap(heap[next],heap[now]);
now=next;
}
}
int get()
{
int res=heap[1];
heap[1]=heap[size--];
int now=1;
while(now<=size/2){
int next=now*2;
if(next<size&&heap[next+1]<heap[next])next++;
if(heap[next]>=heap[now])return res;
swap(heap[next],heap[now]);
now=next;
}
return res;
}
int main()
{
int n,x,y,ans=0;
cin>>n;
for(int i=1;i<=n;i++){cin>>x;put(x);}
for(int i=1;i<n;i++){
x=get(); y=get();
put(x+y);
ans+=x+y;
}
cout<<ans;
return 0;
}