多元Huffman编码问题

多元Huffman编码问题简单(增补空单元)解决

在一个操场的四周摆放着n堆石子,现将石子有次序地合并成一堆。规定每次至少选2堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。

问题分析

求最大费用时只需将石堆排列后从大到小,两两进行合并即可
求最小费用时,将石堆排列后,要尽可能的合并最少的次数且每次合并的石堆数为K堆
在求最小费用时,有时会出现特例,即每次合并K堆,最后一次合并时无法以K堆进行合并,这样的话合并的结果就不是最小费用了,我们要将最小的堆合并最多次这样结果才会最小,所以就要先判断原总堆数是否能使每次合并的堆数都为K堆,如果不能的话就要在原堆数前面加上 X 个个数为0的堆来补齐缺少的堆数
例如共7堆最大合并5堆
石堆数 45 13 12 5 9 22 16
这时排序后为5 9 12 13 16 22 45
如果先合并前5堆 这样结果就为177
如果补零的话 0 0 5 9 12 13 16 22 45,每次合并K堆,结果为148

代码

#include<iostream>
#include<fstream>
using namespace std;
extern void QuickSort(int a[], int p, int r);
void BinarySearchInsert(int a[], int x, int n,int t)
{
 int l = t;
 int left = t;
 int right = n;
 while (left < right)
 {
  left++;
  if (left == n)
  {
   a[t] = x;
   break;
  }
  else if (a[left] < x)
  {
   a[t] = a[left];
   t++;
  }
  else
  {
   a[t] = x;
   break;
  }
 }
}
int Maxcost(int a[],int n)//最大花费合并时合并次数最多,合并从大到小两两进行合并
{
 int max = a[n-1]+a[n-2];
 int m = max;
 while(n--)
 {
  if (n - 2 < 0)
   break;
  else
  {
   m += a[n - 2];
   max += m;
  }
 }
 return max;
}
int Mincost(int a[], int n,int k,int t)//合并次数最少,合并从小到大,以k个堆为一组进行合并
{
 int min =0;
 int m=0,cn=n-t;//m是用来记录上一次合并时的花费,cn用于记录n个数在上一次合并以后还剩余的数量
 int i;
  while (cn >= k)//如果剩余的石堆比k大,为得到最小花费,则不管上一次合并的石堆再选取k个石堆进行合并
  {
   int count = k;//计数用,一次合并k个元素
   for (i = t; i < n; i++)//合并k个石堆
   {
    if (count > 0)
    {
     m += a[i];//记录合并石堆的花费,因为是采用分组合并,每组k个石堆,所以m的值已经包含了前面合并的花费
     t = i;
    } 
    else
     break;
    count--;
    if (count == 0)
    {
     min += m;
     BinarySearchInsert(a, m, n, t);//将合并后的堆重修插入到序列中,以便下次的合并
     m = 0;
    }
    
   }
   cn = cn - k+1;//判断数组中还剩多少元素
  }
 return min;
}
int main()
{
 int n, k,z,p,q=0;//z为可以分的组数,q为需要补0来凑k堆的堆数和,p为如果不能合并为k堆的倍数剩余的堆数
 int a[] = { 0 };
 int b[] = { 0 };
 int maxcost, mincost;
 ifstream myfile("input.txt");//从input.txt文件中读取内容
 ofstream outfile("output.txt", ios::trunc);//声明一个输出流,将想要输出的内容生成一个output.txt文件
 if (!myfile.is_open())
 {
  cout << "can not open this file" << endl;
  return 0;
 }
 myfile >> n;//读取n的值
 myfile >> k;//读取k的值
 for (int i = 0; i < n; i++)
 {
  myfile >> a[i];
  
 }
 
 QuickSort(a, 0, n - 1);
 for (int i = 0; i < n; i++)
 {
  b[i] = a[i];
 }
 maxcost = Maxcost(a, n);
 z = n / k;      //数组中元素每k个一组可以分的组数
 p = (n / k + (n - z * k)) % k;  //计算数组中的元素经过多次合并后还剩余的数量
 if (p==0)
 {
  mincost = Mincost(a, n, k,0);
 }
 else
 {
  for (int j = 0; j < n+k-p; j++)//对原数组进行补零操作
  {
   if (j < k - p)
   {
    a[j] = 0;
   }
   else
   {
    a[j] = b[j - k + p];
   }
  }
  mincost =  Mincost(a, n+k-p, k,0);
 }
 outfile << "最大花费为:" << maxcost << endl;
 outfile << "最小花费为:" << mincost << endl;
 system("pause");
 return 0;
}

测试用例1:
利用文档

测试用例2:

在这里插入图片描述
测试用例3:
在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

likehack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值