大家好,都吃晚饭了吗?我是Kaiqisan,是一个已经走出社恐的一般生徒,今天康康一个贪心算法的经典例题 ---- 切金条问题
问题
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为20的金条,不管切成长度多大的两半,都要花费20个铜板。一群人想整分整块金条,怎么分最省铜板?
例如,给定数组[10,20,30],代表一共三个人,整块金条长度为10+20+30=60.金条要分成10,20,30三个部分。如果,先把长度60的金条分成10和50,花费60再把长度50的金条分成20和30,花费50一共花费110铜板。
但是如果,先把长度60的金条分成30和30,花费60再把长度30金条分成10和20,花费30一共花费90铜板。
所以后者为更优解!
思路
思路:模拟哈夫曼编码,哈夫曼的思想就是这个题目的贪心策略(使用小根堆,每一次取出两个数去合成,加完之后放回去,排好序,重复上面的步骤)
这个思维本质还是贪心算法,先取出最小的两个数,合并完后塞回数据组,然后再取出当前情况下的最小的两个数,再重复上面的所有动作,直到队列中只剩下一个数为止。
比如现在有一个金条 全长为18,需要切成5份,每份的长度分别为 [1, 5, 4, 6, 2]
先把所有的数据塞入小根堆 [1, 2, 4, 5, 6]
先找到现在最小的两个数1, 2 然后加起来,重新塞回队列[3, 4, 5, 6]
然后再次取出最小的两个数 3, 4再次组合,再塞回去 [5, 6, 7]
下面的动作同理 [5, 6, 7] —> [7, 11] — > [18]
最后,队列里面只有一个元素20了,这个20就是最终解
这其实就是把切割金条的动作反过来,(拼接金条?哈哈哈?)
模拟哈夫曼树
代码
static int doGetRes(int[] arr) {
Queue<Integer> minHeap = new PriorityQueue<>(); // 小根堆,在填入元素的时候自动排序
for (int i = 0; i < arr.length; i++) {
minHeap.add(arr[i]);
}
while (true) {
int a = minHeap.poll();
int b = minHeap.poll();
// 队列无元素的时候我不让它再塞回队列,直接返回
if (minHeap.isEmpty()) {
return a + b;
} else {
minHeap.add(a + b);
}
}
}
总结
没有总结,记得了解哈夫曼树的原理!为什么哈夫曼树是贪心策略的核心呢?就是因为它每一次都是取当前最优解而且后期不存在其他可能性的路线反超的机会!