题目大意:
分析:给出一个整数N(2 ≤ N ≤ 500,000),代表有N个字符。接下来有N个数(按升序排列),第i个代表字符i出现的频率。
求出其经哈夫曼编码后的原文长度。
英文原题里都说了怎么做了,大概就是选两个频率最小的点,建一个新点为这两个点的父节点,从序列中删除两个旧点,将新点插入,直至只剩下一个点时,停止操作。
①因为题目满足频率单调性,所以我们可以维护两个数组,第一个为原频率数组A,第二个为辅助数组B。每次我们需要选出“A的首项+A的第二项”,“A的首项+B的首项”,“B的首项+B的第二项”中的最小值,删除这两项,并将新的和加入B末尾,直至A为空后,再将B中剩余的依次合并。
②怎么统计长度?很简单,我们每次合并两项时,把ans加上这两项的和即可。
③long long...
代码略丑...
#include <cstdio>
#include <cstdlib>
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long LL;
const int MAXN = 500009;
const LL INF = 1LL<<60;
int n;
LL q1[MAXN], q2[MAXN];
int l1, r1, l2, r2;
LL ans = 0;
int main()
{
scanf("%d", &n);
l1 = 1, r1 = n;l2 = 1, r2 = 0;
for(int i = 1; i <= n; ++i)
scanf("%I64d", q1+i);
while(l1 <= r1)
{
int flag = 0;
LL Min = INF;
if(l1 < r1)
{
if(q1[l1]+q1[l1+1] < Min)
{
Min = q1[l1]+q1[l1+1];
flag = 1;
}
}
if(l1 <= r1 && l2 <= r2)
{
if(q1[l1]+q2[l2] < Min)
{
Min = q1[l1]+q2[l2];
flag = 2;
}
}
if(l2 < r2)
{
if(q2[l2]+q2[l2+1] < Min)
{
Min = q2[l2]+q2[l2+1];
flag = 3;
}
}
ans += Min;
if(flag == 1) l1 += 2;
else if(flag == 2) l1++, l2++;
else l2 += 2;
q2[++r2] = Min;
}
while(l2 < r2)
{
q2[++r2] = q2[l2]+q2[l2+1];ans += q2[r2];
l2 += 2;
}
printf("%I64d\n", ans);
return 0;
}