神tm鸿雁传书
2018/11/2
问题描述
随着科技的发展,通信的手段日新月异。然而不得不说,至今都没有比鸿雁传书更为深情浪漫的通信方式出现。
两个迷恋地球文明&中华文化的【三体人】出于浪漫主义需求,决定以后通过鸿雁传书的方式来进行通信。为了方便起见,我们姑且叫他们大刘和大白。鸿雁传书,情真意切,大刘和大白的感情也一日比一日深厚。
Fig.1 图文无关
两人的私密通信,自然是“不足为外人道”的。为此大刘煞费苦心地设计了一套用01比特来表示每个三体文字的方式,称为“鸿雁传书字典”。其巧妙之处在于,任何一个01比特串都不会是其他串的前缀串。于是收到一大堆01比特的大白对照着“鸿雁传书字典”,就能原样知道大刘想写的是什么三体文字了。而别人手中没有这个字典,自然不知道这些比特的含义。一个非常简单的(只包含四个可选三体文字)例子Fig.2所示。
Fig.2 合法的“鸿雁传书字典”及其传书过程示例
好景不长,由于三体文字实在是博大精深,有成千上万个。原本的“鸿雁传书字典”是大刘随手设计的,每个三体字都要用好多好多比特来描述。大刘有一次问大白“你吃了没”竟然用了23333个比特,写了一大叠纸,鸿雁送到一半累得吐血掉进黑暗森林再没飞起来。
Fig.3 累得掉进黑暗森林的鸿雁(注意这是三体的鸿雁,长得像鸽子是可以理解的)
大白得知后伤心地哭了很久(●—●),大刘也十分难过。他和大白保证会设计出新的、最好的“鸿雁传书字典”,为此他把自己和大白的所有传书都搜集了起来,并统计出了其中每一个三体文字的出现次数。
做完这一切后,大刘对着这个统计表犯了难,该怎么设计出一个最好的“鸿雁传书字典”,使得平均意义上表示一个三体文字需要的比特数最少呢?你能帮帮大刘么?
输入格式
输入共有 N+1 行。
第 1 行包含一个整数N,代表大刘统计出的不同三体文字的数量。
第 2 行到第 N+1 行每行有一个整数,代表大刘统计的所有书信里,这行对应的三体文字出现的总次数。
注意:
1. 大刘为了请你帮忙,已经把所有三体字按照【1体】-【2体】-【3体】- …… -【N体】的顺序进行了排列,也即第 k 行代表 “【k-1体】” 这个字出现的总次数。
2. N不超过300,000。
输出格式
输出共有 N+1 行。
第 1 行包含一个浮点数F,代表你设计的最优“鸿雁传书字典”中,表示每个三体字需要的平均比特数。保留6位小数。
第 2 行到第 N+1 行每行有一个“01”比特串,代表你设计的最优“鸿雁传书字典”中,表示这行对应三体字的01比特编码。
注意:
1. 输出“鸿雁传书字典”中编码方案的时候,必须也按照【1体】-【2体】-【3体】- …… -【N体】的顺序进行输出,也即和输入文件中的顺序匹配。
2. 表示“鸿雁传书字典”中的编码方案必须使用“0”和“1”来表示,例如“0101”。不接受别的等价描述,例如“2323”。
3. 严格保证输出是N+1行,并且每行中不能有和编码比特无关的空格或其他字符。
输入样例
5
2
1
2
2
3
输出样例
2.300000
111
110
01
00
10
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
typedef struct HTNode {
HTNode * lch, *rch, *father;
long long int weight;
long long int order;
HTNode() :lch(NULL), rch(NULL),father(NULL){}
}HTNode, *HuffmanTree;
typedef char **HuffmanCode;
void createCode(HTNode ** leave, HuffmanCode &HC, long long int n) {
HC = (HuffmanCode)malloc(n * sizeof(char *));
char * code = new char[n];
code[n - 1] = '\0';
long long int i;
for (i = 0; i < n; i++)
{
HTNode * current = leave[i];
HTNode * father =current->father;
long long int start = n - 1;
while (father != NULL)
{
if (father->lch == current)
code[--start] = '0';
else
code[--start] = '1';
current = father;
father = current->father;
}
HC[i] = (char *)malloc((n - start) * sizeof(char));
strcpy(HC[i], code + start);
}
delete code;
}
struct cmp {
bool operator() (HTNode *a, HTNode * b) {
return (a->weight > b->weight);
}
};
priority_queue < HTNode*, vector<HTNode*>, cmp > pque;
void createTree() {
while (pque.size() != 1) {
HTNode *a, *b;
a = pque.top();
pque.pop();
b = pque.top();
pque.pop();
HTNode * temp = new HTNode;
a->father = temp;
b->father = temp;
temp->weight = a->weight + b->weight;
temp->lch = a;
temp->rch = b;
pque.push(temp);
}
}
int main() {
long long int Num, i;
scanf("%lld", &Num);
long long int * Freq = (long long int)malloc(Num * sizeof(long long int));
for (i = 0; i < Num; ++i) scanf("%lld", &Freq[i]);
HTNode ** Leave = (HTNode **)malloc(Num * sizeof(HTNode *));
for(i=0;i<Num;++i) Leave[i] = (HTNode *)malloc(sizeof(HTNode));
for (i = 0; i < Num; ++i) {
Leave[i]->weight = Freq[i];
pque.push(Leave[i]);
}
createTree();
HuffmanCode Code;
createCode(Leave, Code, Num);
long long int sum1 = 0;
long long int sum2 = 0;
float average;
for (i = 0; i < Num; ++i) {
sum1 += strlen(Code[i]) * Freq[i];
sum2 += Freq[i];
}
average = (float)sum1 / sum2;
if (Num == 1) {
average = 1;
strcpy(Code[0], "0");
}
printf("%.6f\n", average);
for (i = 0; i < Num; ++i)
printf("%s\n", Code[i]);
free(Freq);
for (i = 0; i < Num; ++i) free(Leave[i]);
free(Leave);
for (i = 0; i < Num; ++i) free(Code[i]);
free(Code);
return 0;
}
我直接用的STL里的优先级队列,遍历哈夫曼树生成编码那块出于无奈(OJ的最后几个数据量过大)把原本递归方法改写了,虽然丑但是内存少……
算法问题还是另外找时间总结。