这道题题意挺长的,其实就是哈夫曼编码和普通8位编码对内存占用情况的比较。使用哈夫曼编码就是让出现频率高的字母占用的内存尽可能的少,就是一个贪心的思想。貌似之前还没有做过哈夫曼树相关的题目,一开始的想法出了一些问题,没有想清楚哈夫曼树的实现,以为不建树也能直接求出来,不过看来是不行的……这样的话求解思路就很简单了:先统计所有字符出现的次数,根据出现次数建立哈夫曼树,每次贪心的取出两个最小的节点合并成一个新节点直到合并成一个点,当然,还要特判一下只有一种字符的情况,然后从上向下递归求解即可。
#include <iostream>
#include <cstdio>
#include <string.h>
#include <map>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
map<char,int> appear;
char str[10005];
int num[105];
struct Node //哈夫曼树中的节点
{
int value,pos,ls,rs; //当前节点的值,在数组中的位置,左右儿子的下标
Node()
{
value=0,pos=0,ls=0,rs=0;
}
}huffman[10000];
bool operator <(const Node &a,const Node &b) //重载运算符建立最小堆
{
return a.value>b.value;
}
int ans;
void dfs(int pos,int deep)
{
if(!huffman[pos].ls) //如果是叶子节点则更新答案
{
ans+=deep*huffman[pos].value; //当前字符出现次数乘以字段宽度
return;
}
dfs(huffman[pos].ls,deep+1); //递归左儿子
dfs(huffman[pos].rs,deep+1); //递归右儿子
}
int main()
{
//freopen("input.txt","r",stdin);
while(scanf("%s",str)!=EOF)
{
if(strcmp(str,"END")==0)
break;
appear.clear();
ans=0;
priority_queue<Node> q;
memset(num,0,sizeof(num));
memset(huffman,0,sizeof(huffman));
int count=0;
int len=strlen(str);
for(int i=0; i<len; i++)
{
if(!appear[str[i]]) //如果当前字母没有出现过,则给它标号并把出现次数设为1
{
count++;
appear[str[i]]=count;
num[count]=1;
}
else //已经出现过了就直接加吧
{
num[appear[str[i]]]++;
}
}
for(int i=1; i<=count; i++) //把所有节点都放到堆里
{
Node temp;
temp.pos=i;
temp.value=num[i];
huffman[i]=temp; //他们也都是哈夫曼树的叶子节点,先放进去
q.push(temp);
}
if(q.size()==1) //特判只有一种字母的情况
ans=num[1];
while(q.size()>1) //合并这些节点直到只剩一个
{
Node a=q.top();
q.pop();
Node b=q.top();
q.pop();
Node temp;
temp.value=a.value+b.value;
temp.ls=a.pos;
temp.rs=b.pos;
temp.pos=++count;
huffman[count]=temp;
q.push(temp);
}
dfs(q.top().pos,0); //更新答案
printf("%d %d %.1lf\n",len*8,ans,len*8./ans);
}
}