贪心算法思想:
顾名思义,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希 望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本要素:
1.贪心选择性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2. 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法的基本思路:
从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
1. 不能保证求得的最后解是最佳的;
2. 不能用来求最大或最小解问题;
3. 只能求满足某些约束条件的可行解的范围。
实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
找零问题:
问题描述:贪心算法的一个经典案例就是找零问题。你从商店购买了一些商品,找零 63 美分,店员要怎样给你这些零钱呢?如果店员根据贪心算法来找零的话,他会给你两个 25 美分、一个10 美分和三个1 美分(找零金额不能少于1美分)。在没有使用 50 美分的情况下这是最少的硬币数量。
function makeChange(origAmt, coins) { var remainAmt = 0; if (origAmt % 25 < origAmt) { coins[3] = parseInt(origAmt /25); remainAmt = origAmt % 25; origAmt = remainAmt; } if (origAmt % 10 < origAmt) { coins[2] = parseInt(origAmt / 10); remainAmt = origAmt % 10; origAmt = remainAmt; } if (origAmt % 5 < origAmt) { coins[1] = parseInt(origAmt / 5); remainAmt = origAmt % 5; origAmt = remainAmt; } coins[0] = parseInt(origAmt / 1); } function showChange(coins) { if (coins[3] > 0) { console.log("25 美分的数量 - " + coins[3] + " - " + coins[3] * 25); } if (coins[2] > 0) { console.log("10 美分的数量 - " + coins[2] + " - " + coins[2] * 10); } if (coins[1] > 0) { console.log("5 美分的数量 - " + coins[1] + " - " + coins[1] * 5); } if (coins[0] > 0) { console.log("1 美分的数量 - " + coins[0] + " - " + coins[0] * 1); } } var origAmt = 63; var coins = []; makeChange(origAmt, coins); showChange(coins);
makeChange() 函数从面值最高的 25 美分硬币开始,一直尝试使用这个面值去找零。总共用到的 25 美分硬币数量会存储在 coins 数组中。如果剩余金额不到 25 美分,算法将会尝试使用 10 美分硬币去找零,用到的 10 美分硬币总总数也会存储在 coins 数组里。接下来算法会以相同的方式使用 5 美分和1 美分来找零。在所有面额都可用且数量不限的情况下,这种方案总能找到最优解。如果某种面额不可用,比如 5 美分,则会得到一个次优解。