zoukankan      html  css  js  c++  java
  • 算法设计与分析: 4-19 多元Huffman编码问题

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

      对于给定 n 堆石子,编程计算合并成一堆的最大总费用和最小总费用。

      数据输入:
      第 1 行有 2 个正整数 n 和 k,表示有 n 堆石子,每次至少选 2 堆最多选 k 堆石子合并。第 2 行有 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

    • 例题,排序5, 9, 12, 13, 16, 22, 45
    • 最小:
      判断是否需要添加0,不需要,进入加和,5 + 9 + 12 = 26;sum1 = 26,26进队;
      13, 16, 22,26, 45;13 + 16 + 22 = 51;sum1 = 26 + 51 = 77,51进队;
      26, 45, 51,26 + 45 + 51 = 122;sum1 = 77 + 122 = 199,结束。
      最大:
      45, 22, 16, 13, 12, 9, 5 ;
      45 + 22 = 67;sum = 67,sum2 = 67,67进队;
      67 + 16 = 83;sum = 67 + 16 = 83;sum2 = 83 + 67 = 150;150进队;
      83 + 13 = 96;sum = 96 + 150 = 246......

    • 源程序
    • package cn.htu.test;
      /**
       * 本文牵涉到PriorityQueue优先队列,优先队列和堆排序有关简单来说,PriorityQueue这个工具可以自动排序,
       * 当然,它是默认是升序,如果降序的话可以进行比较器的构造,也可以在参数里进行设置(下面的就是对参数进行设置)
       * 另外这里用的泛型是Integer(int的包装类),而不是Interator
       * 优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序,
      可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类,
      对于基本数据类型的包装器类,优先队列中元素默认排列顺序是升序排列,
      但对于自己定义的类来说,需要自己定义比较器
       */
      import java.util.Comparator;
      import java.util.PriorityQueue;
      import java.util.Scanner;
      
      public class DuoYuanHuffmanBianMa {
      
          private static int n,k;//n是有几堆石头,k是最多有多少堆一起合并
          private static int[] numbers;
      
          public static void main(String[] args){
              @SuppressWarnings("resource")
              Scanner input = new Scanner(System.in);
              PriorityQueue<Integer> minQueue;
              
              PriorityQueue<Integer> maxQueue;
      
              while (true){
                  n = input.nextInt();
                  k = input.nextInt();
      
                  numbers = new int[n+1];//不用[0]号元素.numbers用来存放每一堆的石子数
                  minQueue = new PriorityQueue<>(n);//默认升序,因为我们要不断的进行进队,所以要不断的排序,自动排序最好了
                  maxQueue = new PriorityQueue<>(n, Comparator.reverseOrder());//降序
      
                  for(int i=1; i<=n; i++){
                      numbers[i] = input.nextInt();//逐个输入每一堆的石子数
                      minQueue.add(numbers[i]);
                      maxQueue.add(numbers[i]);
                  }
                  
      
                  int max = getMaxValueByQueue(maxQueue);
                  int min = getMinValueByQueue(minQueue);
      
                  System.out.println("Max: "+max);
                  System.out.println("Min: "+min);
              }
          }
      
          //每次选k堆最小的,不足补0
          private static int getMinValueByQueue(PriorityQueue<Integer> queue){
              int sum = 0;
              while(queue.size() % (k - 1) != 1)
                  queue.add(0);///添加0
              while(queue.size() > 1)
              {
                  int temp = 0;
                  for(int i = 0; i < k; i++)
                  {
                      temp += queue.poll();
                      //queue.poll();
                  }
                  sum += temp;
                  queue.add(temp);
              }
      
              return sum;
          }
      
      
          //每次选2堆最大的
          private static int getMaxValueByQueue(PriorityQueue<Integer> queue){
              int sum = 0,temp;
              for(int i=1; i<=n-1; i++){
                  temp = queue.poll() + queue.poll();//队列当中头两个取出来然后合在一堆,temp是临时的费用值,也是合在一起后的新堆石头数
                  sum += temp;//累计每一次使用的费用
                  queue.add(temp);//将新石头堆数加到“会自己排序的”队列当中
              }
      
              return sum;//sum是总和,就是最大费用
          }
      }
    • 对于优先队列;
    • 常用方法:
      peek()//返回队首元素 poll()//返回队首元素,队首元素出队列 add()//添加元素 size()//返回队列元素个数 isEmpty()//判断队列是否为空,为空返回true,不空返回false
    • 参考链接:
    • https://www.cnblogs.com/wei-jing/p/10806236.html
    • https://blog.csdn.net/KO812605128/article/details/102604583?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5.nonecase
    • https://blog.csdn.net/IOIO_/article/details/81076612
  • 相关阅读:
    ASP.NET Forms身份认证
    .net中调用存储过程的示例 带有输出参数
    jquery ajax .net使用
    存储过程实现批量删除
    相关分页Select语句(扩展)
    css中对于文字溢出时的控制
    精打细算过日子 车辆养用如何能更省钱
    2013年最新交通法规全文 具体扣分细节全面解读
    wxpython学习笔记(一)
    wxpython学习笔记(一)
  • 原文地址:https://www.cnblogs.com/shallow920/p/12864567.html
Copyright © 2011-2022 走看看