Greedy(C) //C是问题的输入集合即候选集合 { S={ }; //初始解集合为空集 while (not solution(S)) //集合S没有构成问题的一个解 { x=select(C); //在候选集合C中做贪心选择 if feasible(S, x) //判断集合S中加入x后的解是否可行 S=S+{x}; C=C-{x}; } return S;
例题分析
[活动安排问题]
活动安排问题是可以用贪心算法有效求解的一个很好的例子。该问题要求高效地安排一系列争用某一公共资源的活动。贪心算法提供了一个简单、漂亮的方法使得尽可能多的活动能兼容地使用公共资源。
在下面所给出的解活动安排问题的贪心算法gpeedyselector中,各活动的起始时间和结束时间存储于数组s和f{中且按结束时间的非减序:.f1≤f2≤…≤fn排列。如果所给出的活动未按此序排列,我们可以用o(nlogn)的时间将它重排。
/** * 活动时间安排 */ @Test public void testArrangeActivity() { int[] start = {1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12}; int[] end = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; List<Integer> results = arrangeActivity(start, end); for (int i = 0; i < results.size(); i++) { int index = results.get(i); System.out.println("开始时间:" + start[index] + ",结束时间:" + end[index]); } } /** * 活动安排 * * @param s 开始时间 * @param e 结束时间 * @return */ public List<Integer> arrangeActivity(int[] s, int[] e) { int total = s.length; int endFlag = e[0]; List<Integer> results = new ArrayList<>(); results.add(0); for (int i = 0; i < total; i++) { if (s[i] > endFlag) { results.add(i); endFlag = e[i]; } } return results; }
[找零钱问题]
假如老板要找给我99分钱,他有上面的面值分别为25,10,5,1的硬币数,为了找给我最少的硬币数,那么他是不是该这样找呢,先看看该找多少个25分的,诶99/25=3,好像是3个,要是4个的话,我们还得再给老板一个1分的,我不干,那么老板只能给我3个25分的拉,由于还少给我24,所以还得给我2个10分的和4个1分。
@Test public void testGiveMoney() { //找零钱 int[] m = {25, 10, 5, 1}; int target = 99; int[] results = giveMoney(m, target); System.out.println(target + "的找钱方案:"); for (int i = 0; i < results.length; i++) { System.out.println(results[i] + "枚" + m[i] + "面值"); } } public int[] giveMoney(int[] m, int target) { int k = m.length; int[] num = new int[k]; for (int i = 0; i < k; i++) { num[i] = target / m[i]; target = target % m[i]; } return num; }
[背包问题]
有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
@Test public void testMoveCard() { //总共4堆 int heap = 4; // int[] cards = {9, 8, 17, 6}; int[] cards = {10, 10, 20, 0}; int count = moveCards(cards, heap); System.out.println("移动次数:" + count); for (int i = 0; i < cards.length; i++) { System.out.println("第" + (i + 1) + "堆牌数:" + cards[i]); } } /** * 均分纸牌 * @param cards * @param heap * @return */ public int moveCards(int[] cards, int heap) { //总牌数 int sum = 0; for (int i = 0; i < cards.length; i++) { sum += cards[i]; } //每堆平均牌数 int avg = sum / heap; //移动次数 int count = 0; for (int i = 0; i < cards.length; i++) { if(cards[i] != avg) { int moveCards = cards[i] - avg; cards[i] -= moveCards; cards[i + 1] += moveCards; count++; } } return count; }贪心算法所作的选择可以依赖于以往所作过的选择,但决不依赖于将来的选择,也不依赖于子问题的解,因此贪心算法与其他算法相比具有一定的速度优势。如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。