贪心算法
总结
往往伴随着排序
贪心选择 + 最优子结构(关键)
贪心选择:对子问题进行一系列的局部最优选择。
最优子结构:问题的最终解可以转化为子问题的解。
作当前来看最好的选择,不从整体最优考虑
因此,当尝试使用贪心算法时,无需从全局的角度进行考虑,短视/简单地来看待问题,无需将问题复杂化!
局部最优
DP和贪心
相同点:都要求问题具有最优子结构
不同点:贪心使用自顶向下的方式,通过某种策略选择某一个子问题后,没被选择的子问题直接抛弃,只在上一步选择的基础上继续选择,当前最优 -> 下一阶段的当前最优······(局部最优)显然最终结果不一定是全局最优;而DP从边界开始,考虑了所有的子问题,继承得到最优结果的解,由于重复子问题,后期可能还会继承。
经典问题
区间覆盖问题
区间完全覆盖问题
-
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(闭区间),求最少使用多少条线段可以将整个区间完全覆盖。
-
贪心策略:将问题看成长度为m-1的子区间拓展到长度为m的完整区间,为使用最少的线段,选取满足条件(即左端点<=子区间右端点)的区间中右端点最大的线段(局部最优)。
-
伪代码
curSection.l = 1; curSection.r = 1; sort(sections); // 对区间的左端点进行排序 while(curSection.r <= maxL): for i : 1 to N: if(seciton[i].l <= curSection.r): maxR = i; choose(maxR); curSection.r = maxR + 1;
种树
-
问题描述:一条街的一边有几座房子。因为环保原因居民想要在路边种些树,路边的地区被分割成块,并被编号成1…N;每个部分为一个单位尺寸大小并最多可种一棵树,每个居民想在门前种些树并指定了三个号码B,E,T,这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
-
贪心策略:对于h个区域,若其不交叉,此时总数即为各区域的最低数目;若有交叉,则需要尽可能将树种在交叉区域,则目标可转换为求min{各区域数量和-交叉数},将h分为h-1和1考虑,要使h-1区域与最后一个区域交叉最多,显然是h-1区域将树种在末尾的交叉数量更多。(通过数轴更好理解)
-
其他解法:差分约束系统??
智力大冲浪
-
问题描述:首先,比赛时间分为n个时段(n≤500),它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成(1≤ti≤n)。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。
-
贪心策略:尽可能选择奖励最多(扣钱最多)的游戏完成,而要使完成的游戏数量最大,将游戏的开始时间尽可能靠近最晚开始时间(这里就是结束时间),从而达到局部最优解。
-
解决步骤:
- 先对游戏按奖励金额大小降序排序
- 顺序遍历游戏,找到最靠近最晚开始时间(结束时间)的时间点,将其安排在该点,获得游戏的奖励金额,并对该点作已按安排处理
github地址:https://github.com/jyccccc/PATPATPAT