贪心选择算法为算法分析中一种常用算法,通过一系列的选择来得到一个问题的解。它所作的每一个选择都是当前状态下某种意义的最好选择,即贪心选择。希望通过每次所作的贪心选择导致最终结果是问题的一个最优解。这种启发式的策略并不总能奏效,然而在许多情况下确能达到预期的目的。对于可利用贪心算法解决的问题需要同时满足:最优子结构性质和贪心选择性质。

1.贪心选择性质

所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。在动态规划算法中,每步所作的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能作出选择。而在贪心算法中,仅在当前状态下作出最好选择,即局部最优选择。然后再去解作出这个选择后产生的相应的子问题。贪心算法所作的贪心选择可以依赖于以往所作过的选择,但决不依赖于将来所作的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,我们必须证明每一步所作的贪心选择最终导致问题的一个整体最优解。通常可以用我们在证明活动安排问题的贪心选择性质时所采用的方法来证明。首先考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步作贪心选择,最终可得到问题的一个整体最优解。其中,证明贪心选择后的问题简化为规模更小的类似子问题的关键在于利用该问题的最优子结构性质。

2.最优子结构性质

当一个问题的最优解包含着它的子问题的最优解时,称此问题具有最优子结构性质。问题所具有的这个性质是该问题可用动态规划算法或贪心算法求解的一个关键特征。在活动安排问题中,其最优子结构性质表现为:若a是对于正的活动安排问题包含活动1的一个最优解,则相容活动集合a’=a—{1}是对于e’={i∈e:si≥f1}的活动安排问题的一个最优解。

3.贪心算法与动态规划算法的差异

贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。但是,对于一个具有最优子结构的问题应该选用贪心算法还是动态规划算法来求解?是不是能用动态规划算法求解的问题也能用贪心算法来求解?下面我们来研究两个经典的组合优化问题,并以此来说明贪心算法与动态规划算法的主要差别。

最优装载问题问题描述:

有一批集装箱要装上一艘载重量为C的轮船,其中集装箱i的重量为Wi。要求在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

算法分析:采用重量轻者先装的贪心选择策略,可产生最优装载问题的最优解。

代码实现:

1: class LContainer implements Comparable {

2: private int id; //编号

3: private double weight; //重量

4: private boolean loadFlag = false; //是否装船标识

5:

6: public LContainer(int id,double weight) {

7: this.id = id;

8: this.weight = weight;

9: }

10:

11: /**

12: * 因为要对对象进行排序,所以要实现java.util.Comparator接口的compareTo(T obj)方法,在该方法中自定义排序算法。

13: */

14: public int compareTo(Object obj) {

15: double ww = ((LContainer) obj).getWeight();

16: if (this.weight > ww) {

17: // 当方法返回 1 时 :把参数对象往前放。(前o.weight , 后this.weight)

18: return 1;

19: } else if (this.weight < ww) {

20: // 当方法返回 -1 时 :把参数对象往后放。(前this.weight , 后o.weight)

21: return -1;

22: } else {

23: return 0;

24: }

25: }

26:

27: public void setId(int id) {

28: this.id = id;

29: }

30:

31: public int getId() {

32: return id;

33: }

34:

35: public double getWeight() {

36: return weight;

37: }

38:

39: public void setWeight(double weight) {

40: this.weight = weight;

41: }

42:

43: public void setLoadFlag(boolean loadFlag) {

44: this.loadFlag = loadFlag;

45: }

46:

47: public boolean getLoadFlag() {

48: return loadFlag;

49: }

50: }

51:

52: public class OptLoading {

53: static int count;

54: static LContainer[] container ;

55:

56: static int loading(double[] weight, double c) {

57: container = new LContainer[weight.length] ;

58: for(int i=0;i

59: {

60: container[i] = new LContainer(i,weight[i]);

61: }

62:

63: Arrays.sort(container); // 实现数组排序

64:

65: for (int i = 0; i < container.length&&container[i].getWeight()<=c; i++)

66: {

67: container[i].setLoadFlag(true);

68: count++;

69: c -= container[i].getWeight();

70: }

71: return count;

72: }

73:

74: public static void main(String[] args) {

75: double c = 1000;

76: double[] weight = { 800, 1000, 1001, 200, 100, 400, 600, 50 };

77: int result = loading(weight,c);

78: System.out.println("最优装载集装箱的数量:\n"+result);

79: System.out.println("依次装载的顺序编号如下:");

80: for (int i = 0; i < container.length; i++)

81: {

82: if (container[i].getLoadFlag())

83: {

84: System.out.print(container[i].getId() + " ");

85: }

86: }

87: }

88:

89: }

更多推荐