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

修理牧场

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

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

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

输入格式:

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

输出格式:

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

输入样例:

8
4 5 1 2 1 3 1 1

输出样例:

49

思路:将锯木头的过程逆过来看的话,就是从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);
}
}

由该算法可以维护一个基本有序的最小堆,但是,我们知道,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);
}
}

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

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)可以实现入队:

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;
}
}

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

void insertHeap(int a[], int key, int& length){
length++;
a[length] = 9999999;
decreaseInsert(a, length, key);
}

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

另外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);
}
使用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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值