Advertisement

最小堆实现优先队列解决修理牧场(c/c++)

阅读量:
修理牧场

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要块木头,每块木头长度为整数个长度单位,于是他购买了一条很长的、能锯成块的木头,即该木头的长度是的总和。

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。

请编写程序帮助农夫计算将木头锯成块的最少花费。

输入格式:

输入首先给出正整数(),表示要将木头锯成块。第二行给出个正整数(),表示每段木块的长度。

输出格式:

输出一个整数,即将木头锯成块的最少花费。

输入样例:

复制代码
 8

    
 4 5 1 2 1 3 1 1
    
    
    
    
    AI写代码

输出样例:

复制代码
    49
    
    
    AI写代码

思路:将锯木头的过程逆过来看的话,就是从N块木头中每次挑出两个最短的合成为一个,再从这N-1个木头中重复合成,直至剩一个木头。那么最少花费就是各个合成的木头长度的和。

这个过程显然与求解赫夫曼树大同小异,这里采用最小堆实现的优先队列求解。

最小堆及优先队列

最小堆实现优先队列有不同方法,这里参考的是《算法导论(第三版)》第六章堆排序。

基础知识: 最小堆一般用数组表示,可以证明,其中第i(1 ≤ i ≤ n) 个元素的父节点PARENT(i) = i / 2,左孩子LEFT(i) = i * 2, 右孩子RIGHT(i) = i * 2 + 1。

首先假定以节点i的左孩子为顶点的子树,右孩子为顶点的子树都是最小堆,若结点i不满足最小堆性质,则让该节点的值逐级下降,直至满足最小堆的性质:

复制代码
 void heapfy(int a[], int i, int length){

    
 int mins = i;
    
 int l = i * 2;
    
 int r = i * 2 + 1;
    
 if(l <= length && a[l] < a[i]) mins = l;
    
 if(r <= length && a[r] < a[mins]) mins = r;
    
  
    
 if(mins != i){
    
 int temp = a[i];
    
 a[i] = a[mins];
    
 a[mins] = temp;
    
 heapfy(a, mins, length);
    
 }
    
 }
    
    
    
    
    AI写代码

由该算法可以维护一个基本有序的最小堆,但是,我们知道,i = n / 2 + 1, n / 2 + 2,....n都是叶节点,如果从n / 2 降至1的过程中都调用该函数,就可以将任给的数组建为最小堆:

复制代码
 void buildHeap(int a[], int length){

    
 for(int i = length / 2; i > 0; i--){
    
 heapfy(a, i, length);
    
 }
    
 }
    
    
    
    
    AI写代码

建立最小堆之后,可以实现队列的出队和入队:

复制代码
 int heapPop(int a[], int& length)//出队

    
  
    
 {
    
 int temp = a[1];
    
 a[1] = a[length];//将队尾结点值复制到队首
    
 heapfy(a, 1, --length);//维护长度减一的堆
    
 return temp;
    
 }
    
    
    
    
    AI写代码

通过void decreaseInsert(int a[], int i, int key)可以实现入队:

复制代码
 void decreaseInsert(int a[], int i, int key)//将结点i的值更新为更小值key

    
  
    
 {
    
 if(a[i] < key) return;
    
 a[i] = key;
    
 while(i > 1 && a[i] < a[i / 2]){  //更新后的值可能会违反最小堆的性质,若此则逐级递升,直至符合最小堆性质
    
 int temp = a[i];
    
 a[i] = a[i / 2];
    
 a[i / 2] = temp;
    
 i /= 2;
    
 }
    
 }
    
    
    
    
    AI写代码

由此,只需在队尾新增极大值结点, 再调用上函数即可实现入队:

复制代码
 void insertHeap(int a[], int key, int& length){

    
 length++;
    
 a[length] = 9999999;
    
 decreaseInsert(a, length, key);
    
 }
    
    
    
    
    AI写代码

至此已经可以解决修理牧场问题了。

另外C++ STL提供了优先队列的实现,稍加修改即可方便地实现最小优先队列,在此附上两个版本的源代码。

源代码:

复制代码
 #include <cstdio>

    
  
    
 void heapfy(int a[], int i, int length){
    
 int mins = i;
    
 int l = i * 2;
    
 int r = i * 2 + 1;
    
 if(l <= length && a[l] < a[i]) mins = l;
    
 if(r <= length && a[r] < a[mins]) mins = r;
    
  
    
 if(mins != i){
    
 int temp = a[i];
    
 a[i] = a[mins];
    
 a[mins] = temp;
    
 heapfy(a, mins, length);
    
 }
    
  
    
 }
    
  
    
 void buildHeap(int a[], int length){
    
 for(int i = length / 2; i > 0; i--){
    
 heapfy(a, i, length);
    
 }
    
 }
    
  
    
 int heapPop(int a[], int& length){
    
 int temp = a[1];
    
 a[1] = a[length];
    
 heapfy(a, 1, --length);
    
 return temp;
    
 }
    
 void decreaseInsert(int a[], int i, int key){
    
 if(a[i] < key) return;
    
 a[i] = key;
    
 while(i > 1 && a[i] < a[i / 2]){
    
 int temp = a[i];
    
 a[i] = a[i / 2];
    
 a[i / 2] = temp;
    
 i /= 2;
    
 }
    
 }
    
 void insertHeap(int a[], int key, int& length){
    
 length++;
    
 a[length] = 9999999;
    
 decreaseInsert(a, length, key);
    
 }
    
  
    
 int a[10010] = {0};
    
 int main(){
    
  
    
 int m, n;
    
 scanf(" %d", &n);
    
 int length = n;
    
 for(int i = 1; i != n+1; ++i){
    
 scanf(" %d", &a[i]);
    
 }
    
  
    
 buildHeap(a, length);
    
  
    
 int sum = 0;
    
 while(length > 1){
    
 int x = heapPop(a, length);
    
 int y = heapPop(a, length);
    
 insertHeap(a, x+y, length);
    
 sum += x+y;
    
 }
    
  
    
 printf("%d", sum);
    
 }
    
    
    
    
    AI写代码

使用STL:

复制代码
 #include <cstdio>

    
 #include <queue>
    
 #include <vector>
    
 using namespace std;
    
 int main(){
    
     priority_queue<int,vector<int>,greater<int> >l;
    
     int n,a;
    
     scanf("%d", &n);
    
     for (int i=0;i<n;i++){
    
     scanf("%d", &a);
    
     l.push(a);
    
     }
    
     int sum=0;
    
     while(l.size()!=1){
    
     int x=l.top();
    
     l.pop();
    
     int y=l.top();
    
     l.pop();
    
     sum+=x+y;
    
     l.push(x+y);
    
     }
    
     printf("%d\n",sum);
    
     return 0;
    
 }
    
    
    
    
    AI写代码

全部评论 (0)

还没有任何评论哟~