贪心算法通常也会结合其他知识点一并考察(如排序、栈、堆排序等)
(预备知识)贪心法求解钞票问题
这里有几种不同面额的钞票,1元、5元、10元、20元、100元、200元的钞票无穷多张,现在使用这些钞票去支付X元的面额,问最少需要多少张?
例如X=628,我们通常会尝试从面额最大的钞票(200元)开始尝试,此时发现200元的钞票只能使用3张,此时剩余的面额是628-3*200=28元,再依次查看次最大面额100元,因为100>28,因此不能用100元构成结果,再次以查看20元,发现可以使用1张,······,以此类推,我们得到最终的最小钞票张数的结果是8(628 = 200*3 + 20*1 + 5*1 + 1*3)
如果用暴力枚举的方法,可以验证8确实是最优结果。我们这里使用的就是“贪心”的策略,那么从算法的层面如何解读呢?为什么这个示例中用贪心策略能够得到最优解呢?我们这里引入贪心的思想。
(贪心)
定义:遵循某种规律,不断地选取当前最优策略的设计方法,就是贪心的思想。
思考:为什么上述钞票的例子使用贪心可以得到全局最优解?
原因:因为上述的钞票列表中任意面额都是比自己小的面额的整数倍,每当使用一张较大面额的钞票时,一定需要更多的较小面额钞票取代,因此可以保证:当前最优解就是全局最优解,即贪心成立。
代码实现:
private static final int[] m = {100,50,20,10,5,2,1};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int f = scanner.nextInt();
int[] amount = new int[f];
for(int i = 0 ; i < f;i++){
amount[i] = giveChange(scanner.nextInt());
}
for(int i = 0 ; i < amount.length;i++){
System.out.println(amount[i]);
}
}
public static int giveChange(int n) {
int num=0;
for(int i=0;i<m.length;i++){
num+=n/m[i];
n=n%m[i];
}
return num;
}
变形:若此时增加一张7元面额的钞票,贪心是否还成立?
此时仍然使 X = 628,使用贪心策略的话,得到的解是8 (200*3 + 20*1 + 5*1 + 1*3),然而实际的最优解是6(200*3 + 20*1 + 7*1 + 1*1),此时贪心不成立,那么如何解决贪心不成立的问题,通常用动态规划求解。
简单总结一下:
贪心算法的成立是有条件的,需要满足某种情况(比如倍数关系等)。但是,一旦我们寻找到了贪心的规律,贪心就可以帮助我们快速高效地解决很多困难的问题。若不使用贪心,可能需要用回溯、动态规划、暴力枚举等方法,势必会造成较大的开销。