Greedy Algorithm
贪心算法在每一步的选择中都会采取在当前状态下的最优决策,并希望由此产生的最终结果也是全局最优。
决策:局部最优(以希望能全局最优)
与一般的搜索(也称暴力搜索/蛮力搜索/枚举回溯)和动态规划相比,贪心算法的不同之处在于:它不对整个状态空间进行遍历或计算,而是始终按照局部最优的选择执行下去,不再回头。
搜索和动态规划会遍历整个状态空间。搜索有回溯,是蛮力遍历;动态规划是把状态分阶段+按顺序,然后选取代表+提炼最优子结构,从而更高效地处理所有状态。它两都是基于全局考量的,所以求解一定是对的。
正因为这个特性(不遍历整个状态空间),贪心算法不一定能得到正确的结果,除非可以证明。即证明:按照特定方法做出的局部最优选择,依然可以得到全局最优结果。
贪心,一定要证明。
举个例子,零钱兑换。
题目:给定一个整数数组 coins(表示不同面额的硬币)和一个整数 amount(表示总金额),计算并返回可以凑成总金额的最少硬币个数。如果没有任何一种硬币组合能组成总金额,就返回 -1。假设每种硬币的数量是无限的。
我们平时兑换零钱,是会尽量选择面额大的(因为这样可以更快地兑完)直到它不能兑换了为止,然后剩余的再用小面额的凑齐(策略一致)。
“每次都选尽量大的面值”就是一个贪心思想。
那我们思考,它对吗?不一定对。
比如用 [10,9,1]
兑换 18(状态图如下),如果用贪心策略兑换,就会走图中的红色路径,即需要 9 个硬币(1 个 10 和 8 个 1),而最优解是绿色路径,即需要 2 个硬币(2 个 9)。
如果是暴力搜索,它会遍历所有状态
[剩余金额, 已用硬币数]
,然后取最小的硬币数。虽然搜索也会考虑贪心选的那条路径,但它会遍历所有边所有点。
贪心算法不一定能得到正确的解,在大部分题目上不能随便使用。如果要使用贪心算法,就必须先证明它的正确性。
除了数学归纳法和反证法之外,还有三种常用的证明方法。
决策包容性是从“状态空间”这一本质出发的一个证明方法。包容,即包含,指子集包含。子集,是可达边界构成的子集。
贪心算法实际上是在状态空间中按照局部最优策略找了一条路径。如果我们能证明:从一个点出发,它还能到达最优解,即并不会丢失最优解的可达性。那么,这样的贪心就是对的。
比如用 [5,10,20]
来兑换零钱,贪心算法是成立的,因为它们的面值互成倍数,此时能用 1 张 10 块兑换的必然也能用 2 张 5 块的来兑换。在这种情况下,“每次都选尽量大的面值”的局部最优策略是有包容性的。
再来看两个例子。
题目1:柠檬水找零。每杯柠檬水售价为 5,给定一个整数数组 bills,bills[i] 里存着第 i 位顾客付的账,值只会是 5、10、20,判断能否给每位顾客正确找零。注意:一开始手头并没有任何零钱。
代码如下:
var lemonadeChange = function(bills) {
const moneys = {
5: 0,
10: 0,
20: 0
};
// 找零:用贪心策略 “在可选的面额里取最大的”
const exchange = function(amount) {
for (let x of [20, 10, 5]) {
// 用循环减法替代除法和%
while (amount && x <= amount && moneys[x] > 0) {
amount -= x;
moneys[x]--;
}
}
return amount === 0;
};
// 遍历账单
for (let x of bills) {
if (!exchange(x - 5)) return false;
moneys[x]++;
}
return true;
};
题目2:分发饼干。给定一个孩子的胃口数组 g 和一个饼干的尺寸数组 s,求能满足最多孩子的个数。孩子 i 的胃口值 g[i],饼干 j 的尺寸值 s[j],如果 s[j] >= g[i],那就可以将这个饼干 j 分配给孩子 i ,此时孩子 i 会得到满足。
代码如下:
var findContentChildren = function(g, s) {
// 原地排序:小-大
g.sort((a, b) => a - b);
s.sort((a, b) => a - b);
let ans = 0;
let j = 0, sn = s.length;
// 贪心策略:对于孩子 child,选择一个能满足其胃口的“最小”饼干
for (let child of g) {
while (j < sn && child > s[j]) j++;
if (j >= sn) break;
ans++;
j++;
}
return ans;
};
以上两个例子,能都用决策包容性来证明贪心理论的正确性,所以可以用贪心算法求解。
决策包容性,只看一条边。
在思考贪心算法时,有时候不容易直接证明局部最优决策的正确性,此时可以往后多扩展一步,进而对当前的决策进行验证。扩展决策范围,即往后多看一步。
决策扩展范围,是看两条边。
来看两个例子。
题目1:跳跃游戏。给定一个非负整数数组 nums,其中 nums[i] 表示在该位置可以跳跃的最大长度。起初是位于数组的第一个位置,求跳到数组最后一个位置的最少跳跃次数。
代码如下:
var jump = function(nums) {
// 目的地:最后一个位置的下标
let target = nums.length - 1;
let ans = 0;
let cur = 0;
while (cur < target) {
let jumping = cur + nums[cur];
// 如果本位置跳一下就能到达目的地,则直接返回+1
if (jumping >= target) return ans+1;
// 否则,判断下一步跳哪里
// 贪心策略:在当前位置的可跳范围内,选一个能跳“最远”的那个位置
let farest = -1;
let next = -1; // 下个位置
for (let j = cur + 1; j <= jumping; j++) {
if (j + nums[j] > farest) {
farest = j + nums[j];
next = j;
}
}
// 卡这里了,走不动了,也到不了目的地了
if (next === -1) return -1;
// 否则,跳至下个位置,步数+1
cur = next;
ans++;
}
return ans;
};
题目2:买卖股票的最佳时机。给定一个数组 prices,其中 prices[i] 表示股票第 i 天的价格,求能获得的最大利润。说明:任何时候最多只能持有一股股票,在每一天中可以购买或出售,不限交易次数。
代码如下:
var maxProfit = function(prices) {
let ans = 0;
for (let i = 0; i < prices.length - 1; i++){
// 贪心策略:如果明天涨,那就买入(以此获得所有上涨区间的全部收益)呃~预言家式炒股
ans += Math.max(prices[i + 1] - prices[i], 0);
}
return ans;
};
以上两个例子,都能用“扩展决策范围+决策包容性”来证明贪心理论的正确性,所以可以用贪心算法求解。
多用于求顺序的题目,即按照某个顺序来完成一个任务。贪心理论认为按照某个顺序排列是比较优的,此时要证明它,就可以用邻项交换法。
证明:无论什么情况下,只要沿着逆序方向走,就会让答案变差。
这样就能证明按有序的方向走,必然能得到最优解。贪心算法的实现就是碰到逆序的就给它局部邻项交换下,此时得到的序列就是最优序列。
至于“逆序/有序”的定义,就得看具体场景了。举个例子。
题目1:完成所有任务的最少初始能量。给定一个任务数组 tasks,tasks[i] = [actuali, minimumi],其中,第一个元素表示完成第 i 个任务需要耗费的实际能量,第二个元素表示开始第 i 个任务前需要达到的最低能量。求完成所有任务的最少初始能量。
代码如下:
var minimumEffort = function(tasks) {
// 贪心策略:按元素的 actual - minimum 升序排列,以此顺序完成任务
tasks.sort((a, b) => a[0] - a[1] - (b[0] - b[1]));
// 倒着计算初始能量
let ans = 0;
for (let i = tasks.length - 1; i >= 0; i--) {
ans = Math.max(ans + tasks[i][0], tasks[i][1]);
}
return ans;
};
贪心算法在每一步的决策中都采取局部最优策略,并希望由此导致的最终结果也是全局最优的。它不会遍历整个状态空间,因此贪心算法不一定能得到正确的结果,除非可以证明。
如果要使用贪心算法,就必须先证明它的正确性。除去熟知的数学归纳法和反证法之外,还有三种重要的证明方法,分别是决策包容性、扩展决策范围和邻项交换法。它们也可以结合使用,以证明做什么决策是最好的。
搜索、动态规划和贪心是一脉相承的,都基于状态。不同之处在于搜索和动态规划会遍历整个状态空间,而贪心不会。正因为如此,贪心算法得到的结果不一定正确(除非可以证明),而基于全局状态的算法求出的最优解一定是正确的。
用贪心能做的题目,一般也都可以用搜索或者动态规划来求解,只是贪心一般是最高效的。所以,遇到问题,先想想搜索和动态规划,先对整体的状态空间有个了解。如果时间复杂度太高或是运行太慢,再考虑贪心(此时,也可能会更容易一些)。搜索→动态规划→贪心,是一个比较好的思路。
还是要格外强调:任何时候做题不要先想贪心。因为在不考虑时间复杂度的情况下,用搜索和动态规划总是可以求解的。同时,贪心一定要证明。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/n2-BgGu6gt081rtk9CIr0g
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。