前言
本文将介绍使用贪心算法计算一个黄金分割花费最小代价的算法题。
正文
题目描述
把一块金条切成两块,花费的代价是和金条长度一样的钱,比如,长度为20的金条,不管如何切都需要花费20块钱。给出一块金块,如何分最省钱?
例如:给出数组[10,20,30],代表要把金条分成3块,整块金条长为60,要分成10、20、30长度的3块。
思路分析
按照题目描述,我们来看下比较简单的切分方法:
- 如果把长度为60的金条分成10和50两部分,需要花费60元,再把长度为50的金条分成20和30,需要花费50元,这种切法一共需要花费110元。
- 如果把长度为60的金条分成30和30,需要花费60元,再把长度为30的金条分成10和20,需要花费30元,这种切法需要花费90元。
这两种方法,第二种比第一种花费更少,比第一种更优。
看到这里,我们野马般的思绪和脑洞又来了,得出了一个结论:先切大的!
先切大的是一种贪心策略,但是这个贪心策略是有问题的。下面来看一个反例,假如要切成4块:[97,98,99,100],如果先切大的,需要花费786元,但先切195和199花费590元,所以先切大的是不正确的。
那么如何切分呢?
这就使用到大名鼎鼎的哈夫曼编码了,下面来看下如何使用哈夫曼编码进行分割。
假如有个数组[2,1,7,3,4,2,1],总长度为20。来看下这个数组是如何切分:
- 先准备一个小根堆,把数据放到小根堆中,此时小根堆数据为[1,1,2,2,3,4,7]
- 弹出两个元素,并计算两个元素和,并把元素和放到堆中,此时小根堆数据为[2,2,2,3,4,7]
- 重复上一步操作,直到小根堆中只有一个元素时停止。
- 此时小根堆就是最优的分割方案,如下图所示,图中浅棕色值相加就是切割的代价。
代码实现
根据上面的分析,来看下代码实现,代码实现非常简单,使用小根堆几行代码就实现了,代码如下:
public int lessMoney(int[] arr) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
priorityQueue.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (priorityQueue.size() > 1) {
cur = priorityQueue.poll() + priorityQueue.poll();
sum += cur;
priorityQueue.add(cur);
}
return sum;
}
总结
本文将介绍使用贪心算法计算一个黄金分割花费最小代价的算法题,在实现的过程中使用了哈夫曼编码算法,关于该算法的证明这里就不展开介绍了,本文的关键点在于贪心策略的选取,比如文中介绍的两种常见的策略,但是却不是最优的。
另外,本文介绍的题还可以使用暴力递归的方式来处理,将每种情况都进行计算选出数值最小的即可,这里就不展开介绍了。