#include <iostream>
#include<stdio.h>
#include <cstring>
using namespace std;
struct{
int self;
int parent;
int left;
int right;
}tree[100];
int main()
{
string s;
int arr[35];
int i,j,k,a,b,c,d,e,f,g;
double A;
while(cin>>s&&s!="END")
{
memset(arr,0,sizeof(arr));
a=s.size();
for(i=0;i<a;i++)
{
arr[s[i]-64]++;
}
for(i=1,j=1;i<35;i++)
{
if(arr[i]!=0)
{
tree[j].self=arr[i];
tree[j].parent=tree[j].left=tree[j].right=0;
j++;
}
}
j--;
for(i=j+1;i<j*2;i++)
tree[i].parent=tree[i].left=tree[i].right=0;
for(i=j+1;i<j*2;i++)
{
b=c=10000;
for(k=1;k<i;k++)
{
if(tree[k].self<b&&tree[k].parent==0)
{
c=b;
e=d;
b=tree[k].self;
d=k;
}
else if(tree[k].self<c&&tree[k].parent==0)
{
c=tree[k].self;
e=k;
}
}
tree[i].self=b+c;
tree[i].right=d;
tree[i].left=e;
tree[d].parent=i;
tree[e].parent=i;
}
f=0;
for(i=1;i<j+1;i++)
{
k=i;
g=0;
while(tree[k].parent!=0)
{
k=tree[k].parent;
g++;
}
f+=g*tree[i].self;
}
if(j==1)
f=a;
A=(double)(a*8)/f;
a=a*8;
printf("%d %d %.1f\n",a,f,A);
}
return 0;
}
题目代号 :D(1003)
简单题意:题目任意给出一串字符,以END为结束。字符内容包括有26个大写字母和一个‘_'。要求是算出8乘以字符数,再除以把字符串的字符按哈夫曼树组成的编码总数,结 果保留一位小数。案例可以看出输入输出的规则。
解题思路:首先,求出字符数,那是必须要做的,无论是被除数需要还是为以后的循环做准备都有必要。其实深入思考就可以知道,解决此问题有2个大难题,第一,把重复的 字符表示提取出来而且还要知道每个字符的重复次数。第二,贪心算法来求哈夫曼树,并表示出来,计算出编码总数。第一个问题不是很难,在大一C++考试好像 就是专门考到过这样的题目,用一个数组套一个字符串然后减掉A再加加最后循环即可。第二个问题,那这个就是个大的问题了。首先根据哈夫曼树的特性,创建个 结构体 吧,里面有4个属性,它本身的权,它的左右儿子,它的父母。而后创建个结构体的数组。一开始,我们需要初始化,也就是将整数数组给结构体的属性赋权 值,其他属性清0。其次,扩展一下结构体的数组,因为我们一开始只给有权值的赋值了,但是根据哈夫曼树,有多少个叶子,圆圈总数是叶子树的二倍减一,所以 要把扩展的 除权值以外的属性清零。下面的步骤才是重点:循环的目的是找到已有数组里的最小两个权值,然后把这2个作为此左右儿子,此为他们的父母,此权值 也会成为他们的权值和。把此放入数组,再进行循环,直到数组满位置,找到树根。最后的求编码值,思路是:找到一开始时候的数组,也就是叶子树,从每个叶子 开始,循环找到 它离根的距离,即就是它的编码位数,然后循环此数组就可以实现。输出用C语言更加简单。
做题感想:其实这题比较难了,为什么选这道题呢?其实我也有自己的想法,主要是想有个好一点的做题的开头吧,鼓励自己要坚持下去。上课那天的前天刚得了肠胃炎挂了水 还没有好,所以那天上课昏昏沉沉的,也没有怎么听明白,回来自己看的课件弄的。同学舍友在选课的时候就劝我别选,但我觉得有用就选了。这学期刚开始我就觉 得压力很大了,主要是专业课一下子转难了,没适应过来,其次我还和舍友一起报了驾校,再者我周六还有辅修,所以做题的时间非常少了,但我觉得时间总可以 挤出来的。同学舍友看我那么忙又要劝我退,我还是想学学。这道题我用了2天的时间,查阅了哈夫曼树,改了好多次才A的,感觉很不错,毕竟自己很喜欢成功这 种感觉,虽然代价有点大,但毕竟学到点东西了。大学该留下点什么的,ACM确实是个不错的选择,我希望自己能一直坚持下去,就算没有时间做题,咬着牙,男人 不会半途而废!