哈夫曼编码oj(堆)&多元Huffman编码问题(贪心)

数据结构实验之二叉树六:哈夫曼编码
Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description
字符的编码方式有多种,除了大家熟悉的ASCII编码,哈夫曼编码(Huffman Coding)也是一种编码方式,它是可变字长编码。该方法完全依据字符出现概率来构造出平均长度最短的编码,称之为最优编码。哈夫曼编码常被用于数据文件压缩中,其压缩率通常在20%~90%之间。你的任务是对从键盘输入的一个字符串求出它的ASCII编码长度和哈夫曼编码长度的比值。

Input
输入数据有多组,每组数据一行,表示要编码的字符串。
Output
对应字符的ASCII编码长度la,huffman编码长度lh和la/lh的值(保留一位小数),数据之间以空格间隔。
Sample Input
AAAAABCD
THE_CAT_IN_THE_HAT
Sample Output
64 13 4.9
144 51 2.8
Hint

Source
xam
C++ 可以用优先队列
还可以用堆 或 普通做法
核心思路是一样的

普通做法

#include <stdio.h>
#include <string.h>
void sort(int a[],int l,int r)//快排
{
    int x=a[l],i=l,j=r;
    if(i>=j) return ;
    while(i<j)
    {
        while(i<j&&a[j]>=x) j--;
        a[i]=a[j];
        while(i<j&&a[i]<=x) i++;
        a[j]=a[i];
    }
    a[i]=x;
    sort(a,l,i-1);
    sort(a,i+1,r);
}
int main()
{
    int i,k,kk,sum1,sum2;
    char s[505];
    int num[505],quee[505];
    while(gets(s)!=NULL)
    {
        int len=strlen(s);
        memset(num,0,sizeof(num));
        memset(quee,0,sizeof(quee));
        for(i=0;i<len;i++)
        {
            num[(int)s[i]]++;//记录每个字符出现次数
        }
        kk=0;k=0;
        sum1=len*8;//ASCII 每个字符长度为8
        for(i=0;i<500;i++)
        {
            if(num[i]!=0)
            {
                quee[kk++]=num[i];//数组模拟队列
            }
        }
        sum2=0;
        while(k<kk-1)
        {
            int a1,a2;
            sort(quee,k,kk-1); //队列中元素从小到大排序
            a1=quee[k++];
            a2=quee[k++];
            quee[kk++]=a1+a2; //取出两个求和再放入队列
            sum2+=a1+a2;
        }
        printf("%d %d %.1lf\n",sum1,sum2,(double)sum1*1.0/sum2);

    }
    return 0;
}

堆的做法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int size,a[50005];
void insert(int x)
{
    int i,t;
    a[++size]=x;
    for(i=size; i!=0; i/=2)
    {
        if(a[i]<a[i/2])
        {
            t=a[i];
            a[i]=a[i/2];
            a[i/2]=t;
        }
        else break;
    }
}
void adjust (int a[],int size)
{
    int i,t,p;
    for(i=1; i*2<=size; i=p)
    {
        if(a[i*2]>a[i*2+1]&&i*2+1<=size)
        {
            p=i*2+1;
        }
        else p=i*2;
        if(a[p]<a[i])
        {
            t=a[i];
            a[i]=a[p];
            a[p]=t;
        }
        else break;
    }
}
void delete()
{
    int i,t,p;
    a[1]=a[size--];
    for(i=1; i*2<=size; i=p)
    {
        if(a[i*2]>a[i*2+1]&&i*2+1<=size)
        {
            p=i*2+1;
        }
        else p=i*2;
        if(a[p]<a[i])
        {
            t=a[i];
            a[i]=a[p];
            a[p]=t;
        }
        else break;
    }
}
int main()
{
    int i,z,num[505],len;
    char s[5005];
    double y;
    while(scanf("%s",s)!=EOF)
    {
        int sum=0,summ=0;
        size=0;
        memset(a,0,sizeof(a));
        memset(num,0,sizeof(num));
        len=strlen(s);
        summ=len*8;
        for(i=0; i<len; i++)
        {
            z=s[i];
            num[z]++;
        }
        for(i=0; i<500; i++)
        {
            if(num[i]!=0)
            {
                insert(num[i]);
            }
        }
        int b,d,c;
        while(a[size]!=0)
        {
            b=a[1];
            delete();
            if(a[size!=0])
            {
                c=a[1];
                delete();
                d=b+c;
                insert(d);
                sum=sum+d;
            }
        }
        y=(summ+0.0)/(sum+0.0);
          printf("%d %d %.1lf\n",summ,sum,y);
    }
    return 0;
}

优先队列
首先要包含头文件#include, 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队。
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。

priority_queue <int,vector<int>,greater<int> >//小顶堆 从小到大
priority_queue <int,vector<int>,less<int> > //大顶堆 从大到小

必须有空格 否则像右移符号
当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。

#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
int main()
{
    int num[505],i,sum1,sum2;
    char s[505];
    while(gets(s)!=NULL)
    { //优先队列
        priority_queue<int,vector<int>,greater<int> > qu;
        int len=strlen(s);
        memset(num,0,sizeof(num));
        for(i=0; i<len; i++)
        {
            num[(int)s[i]]++;//记录每个字符出现次数
        }
        sum1=len*8;//ASCII 每个字符长度为8
        for(i=0; i<500; i++)
        {
            if(num[i]!=0)
            {
                qu.push(num[i]);
            }
        }
        sum2=0;
        while(!qu.empty())
        {

            int a1,a2;
            a1=qu.top();
            qu.pop();
            if(qu.empty()) break;//只剩一个元素的时候就可以停了
            a2=qu.top();
            qu.pop();
            qu.push(a1+a2);
            sum2+=a1+a2;
        }
        printf("%d %d %.1lf\n",sum1,sum2,(double)sum1*1.0/sum2);
    }
    return 0;
}

B - 多元Huffman编码问题
Description
在一个操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次至少选2 堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。
对于给定n堆石子,计算合并成一堆的最大总费用和最小总费用。

Input
输入数据的第1 行有2 个正整数n和k(n≤100000,k≤10000),表示有n堆石子,每次至少选2 堆最多选k堆石子合并。第2 行有n个数(每个数均不超过 100),分别表示每堆石子的个数。

Output
将计算出的最大总费用和最小总费用输出,两个整数之间用空格分开。

Sample
Input
7 3
45 13 12 16 9 5 22
Output
593 199
Hint
请注意数据范围是否可能爆 int。
Maxsum 就是普通的2个归一堆
Minsum 取最大k个归一堆,当不能正好归并时,在前面补0(前面补0比在后面补0 小)

#include <iostream>
#include <queue>
#define ll long long
using namespace std;
int fun(int n,int k)//关键 为了优先队列中的所有个数(不止n个,还有每少k个新增的)能被k整除
{                   //不够的在前面补0 
    int m=n;
    int z=m/k;
    m=m%k+z;
    while(m>k)
    {
        m=m-k+1;
    }

    return m;
}
int main()
{
    int n,k,x,i;
    //默认是大顶堆 降序less
    //priority_queue<int> qu;
    priority_queue<ll,vector<ll>,less<ll> >qu;
    priority_queue<ll,vector<ll>,greater<ll> > pu;
    cin>>n>>k;
    for(i=0;i<n;i++)
    {
        cin>>x;
        qu.push(x);
        pu.push(x);
    }
    ll Maxsum=0;
    while(qu.size()!=1)
    {
        ll a=qu.top();qu.pop();
        ll b=qu.top();qu.pop();
        qu.push(a+b);
        Maxsum+=a+b;
    }
    int z=fun(n,k);//cout<<"z="<<z<<endl;
    if(z!=k)
    {
        for(i=1;i<=k-z;i++)
            pu.push(0);
    }
    ll Minsum=0;
    while(pu.size()!=1)
    {
        ll cn=0;
        for(i=1;i<=k;i++)
        {
            cn+=pu.top();pu.pop();
        }
        pu.push(cn);
        Minsum+=cn;
    }
    cout<<Maxsum<<" "<<Minsum<<endl;
    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值