贪心算法
贪心算法:从一个局部方向进行考虑,通过局部最优即可达到所要的整体最优,该方法就称作贪心算法
贪心算法的几道常见例题:
1.会议问题:已知一系列会议的起始时间和结束时间,在一段时间内合理安排会议,使这段时间可以尽可能多的会议
* 采用贪心算法策略:
* 即每次都选择大于当前时间内结束时间最早的那个会议
代码及解析:
1 public static class Program{ 2 int begin;//会议开始时间 3 int end;//会议结束时间 4 5 public Program(int begin,int end) { 6 this.begin = begin; 7 this.end = end; 8 } 9 } 10 11 public static class ProgramComparator implements Comparator<Program> { 12 public int compare(Program p1,Program p2) { 13 return p1.end - p2.end; 14 } 15 } 16 17 18 public static int bestArrange(Program[] programs,int timePoint) { 19 Arrays.sort(programs,new ProgramComparator()); 20 int ans = 0; 21 for (int i = 0; i < programs.length; i++) { 22 if (timePoint <= programs[i].end) { 23 ans++; 24 timePoint = programs[i].end; 25 } 26 } 27 return ans; 28 }
2.最小字符串拼接:给定一个字符串数组,选择一种合适的拼接顺序,将字符串数组合成一个字典序最小的字符串
* 贪心算法策略:
* 1.一种错误策略:直接将字符串数组的每个字符串按字典序从小到大排列后,再依次进行拼接。
* 错误的反例:"ba"与"b",按照该策略则拼出字符串bba,但最优解是bab
* 2.正确的贪心算法策略:要比较字符串A和B谁先拼接,就先将两者分别拼接为AB和BA,如果AB字典序小,则说明
* A先拼接,反之则说明B先拼接(先拼接的就将其在排序中放在前面)
代码及解析:
1 public static class StringComparator implements Comparator<String> { 2 public int compare(String s1,String s2) {//重写字符串的比较器即可 3 return (s1 + s2).compareTo(s2 + s1); 4 } 5 } 6 7 public static String lowerString(String[] strs) { 8 if (strs == null || strs.length == 0) { 9 return null; 10 } 11 Arrays.sort(strs,new StringComparator()); 12 String ans = ""; 13 for (int i = 0; i < strs.length; i++) { 14 ans += strs[i]; 15 } 16 return ans; 17 }
3.分割金条问题:有一根金条,将其分为长度确定的若干段,每次进行切割所花费的金钱等于所切割的金条的长度,选择一种方法,使得切割出金条后所花费的金钱最少
* 贪心算法策略:
* 因为要花的钱最少,所以每次尽量先把长的金条分出来,这可以让每次的金条长度减少量最多,使得后续花费更少
* 所以策略就是每次都切当前所需要的最长的金条即可。
*
* 为了便于编码解决问题,我们采用逆向思维进行思考:
* 将金条切割成若干段可以理解为将若干段金条合并成一整段金条,每次花费的代价就是合并出来的金条的代价。
* 所以为了花费的代价尽可能少,每次先把最短的两根金条先合并,最后再合并最长的金条
代码及解析:
1 public static int lessSegCost(int[] arr) { 2 PriorityQueue<Integer> pq = new PriorityQueue<>(); 3 int ans = 0; 4 for (int i = 0; i < arr.length; i++) { 5 pq.add(arr[i]); 6 } 7 while (pq.size() > 1) { 8 int a = pq.poll(); 9 int b = pq.poll(); 10 ans += a + b; 11 pq.add(a + b);//合并成的金条也是要放入优先队列中进行后续处理,不要遗漏 12 } 13 return ans; 14 }
4.投资项目问题:给定一系列项目,包含启动资金和项目利润,并给定能做的项目数,求能获得的最大利润
* 贪心算法:
* 即在力所能及(本金足够)的情况下,去做利润尽可能大的项目即可.
代码及解析:
1 public static class Node{ 2 int cost; 3 int profit; 4 5 public Node(int cost,int profit) { 6 this.cost = cost; 7 this.profit = profit; 8 } 9 } 10 11 public static class NodeComparator1 implements Comparator<Node> {//从开销小的项目开始找起 12 public int compare(Node n1,Node n2) { 13 return n1.cost - n2.cost; 14 } 15 } 16 17 public static class NodeComparator2 implements Comparator<Node> {//在本金允许范围下,找高利润项目 18 public int compare(Node n1,Node n2) { 19 return n2.profit - n1.profit; 20 } 21 } 22 23 public static int getMaxMoney(int W,int k,int[] cost,int[] profit) { 24 PriorityQueue<Node> minCost = new PriorityQueue<>(new NodeComparator1()); 25 PriorityQueue<Node> maxProfit = new PriorityQueue<>(new NodeComparator2()); 26 for (int i = 0; i < cost.length; i++) { 27 minCost.add(new Node(cost[i],profit[i])); 28 } 29 for (int i = 0; i < k; i++) {//最多可以做k个项目 30 //将本金允许的项目全部解锁 31 while (minCost.size() > 0 && minCost.peek().cost <= W) { 32 maxProfit.add(minCost.poll()); 33 } 34 if (maxProfit.size() == 0) {//当前本金已经没有项目可做了 35 return W; 36 } 37 W += maxProfit.poll().profit; 38 } 39 return W; 40 }