浅谈贪心算法

news/2024/11/14 9:09:44/文章来源:https://www.cnblogs.com/SXqwq/p/18542720

浅谈贪心算法

贪心算法,指在问题求解时,每一步都做出“当前看起来最好的决策”。它没有固定的算法模板,灵活性强。在 OI 领域,无论是入门组,还是省选,NOI,或多或少都出过贪心题。可见贪心的重要性之大。

使用贪心算法解决问题,必须满足 “无后效性”。满足 “无后效性” 不一定当前的决策对后续选择绝对无影响,只要满足当前决策不会影响后续最优解的选择即可。反之,动态规划问题必须满足当前决策对后续决策绝对无影响。在动态规划问题中,我们开多层状态,主要目的在于满足无后效性。

诚然,贪心策略的选择是重要的,但结论的证明也不可忽视。下文将给出部分贪心常用证明方法。

证明

邻项交换法

此类题目往往需确定一个决策顺序,当交换两者决策顺序,不影响其他决策时,可使用邻项交换法确定决策顺序。

例题精讲

共有 \(n\) 个物品,每个物品有两个值,分别为 \(w_i,s_i\)
你可以对这 \(n\) 个物品任意排序。每个物品的贡献 \(W_i=\sum\limits_{j=i+1}^n w_j-s_i\),最小化 $\max{W_i} $ 。
\(1\le n \le 5\times 10^4\)
Source

一看数据范围,排序题。大概率是按照某种策略进行排序然后模拟。

难点在于排序策略。当然多重贪心加上随机化有可能获得可观的分数。

我们用邻项交换的方式思考本题。因为不难发现,交换 \(i,i+1\) 物品后,不会对其他物品产生影响。

从上到下的物品贡献可以表示为 \(\sum\limits_{k=1}^{i-1}w_k-s_i\)

下文记 \(sum=\sum\limits_{k=1}^{i-1}w_k\)

交换前,两个物品的贡献分别为 \(sum-s_i,sum+w_i-s_{i+1}\)

交换后则分别为 \(sum-s_{i+1},sum+w_{i+1}-s_i\)

因此,当满足

\[\max\{sum-s_{i+1},sum+w_{i+1}-s_{i}\}<\max\{sum-s_{i},sum+w_{i}-s_{i+1}\} \]

交换可能会使答案更优。

下面的操作基于以下原则,需要记住。

\(\max(a,b)<c \Leftrightarrow a<c\text{且}b<c\)

\(\max(a,b)>c \Leftrightarrow a>c\text{或}b>c\)

\(\max(a+b,a+c)=a+\max(b,c)\)

因此,上式可以消去 \(sum\),得

\[sum+\max(-s_{i+1},w_{i+1}-s_{i})<sum+\max(-s_{i},w_{i}-s_{i+1}) \]

也就得到了交换条件。

\[\max(-s_{i+1},w_{i+1}-s_{i})<\max(-s_{i},w_{i}-s_{i+1}) \]

依据 \(\max(a,b)>c \Leftrightarrow a>c\text{或}b>c\),我们将上式拆开。

\[-s_{i+1}<\max(-s_i,w_{i}-s_{i+1}) \]

\[w_{i+1}-s_{i}<\max(-s_i,w_{i}-s_{i+1}) \]

因为 \(s_i,w_i,s_{i+1},w_{i+1}\) 恒大于 \(0\),故下列条件一定满足。

\[-s_{i+1}<\max\{-s_i,w_i-s_{i+1}\} \]

\[-s_i<\max\{-s_{i+1},w_{i+1}-s_i\} \]

证明还是依据 \(\max(a,b)>c \Leftrightarrow a>c\text{或}b>c\)。这里不再赘述。

刚才提到,若交换条件

\[sum+\max(-s_{i+1},w_{i+1}-s_{i})<sum+\max(-s_{i},w_{i}-s_{i+1}) \]

成立,则一定有

\[-s_{i+1}<\max(-s_i,w_{i}-s_{i+1}) \]

\[w_{i+1}-s_{i}<\max(-s_i,w_{i}-s_{i+1}) \]

成立

前面已经证明,一式一定成立,我们只需要关心第二个式子。

还是把它拆开,显然 \(w_{i+1}-s_i<-s_i\) 是一定成立的,我们只需要关心 \(w_{i+1}-s_i<w_i-s_{i+1}\)

移项,得。

\[w_{i+1}+s_{i+1}<w_i+s_i \]

证毕.

归纳法

数学归纳法

一般的,证明一个与正整数 \(n\) 有关的命题,可使用数学归纳法。具体步骤如下。

  • (归纳奠基)证明当 \(n=n_0(n_0\in\operatorname{\N_{+}})\) 时成立。
  • (归纳递推)以 “当 \(n=k(k\in \operatorname{\N_{+}},k\ge n_0)\) 时命题成立” 为条件,推出 “当 \(n=k+1\)” 时命题也成立。

由上述二步骤即可断定命题对 \(n_0\) 开始的所有正整数 \(n\) 都成立

正确性. 由皮亚诺公理中 “归纳公理“ 可得,若某个性质在 \(0\) 成立,并且对于任意自然数 \(n\),若该性质在 \(n\) 成立,则它在 \(S(n)\) 也成立,那么该性质对于所有自然数都成立。事实上,数学归纳法和 “归纳公理” 本质相同,这也就意味着,数学归纳法作为一个公理,正确性是毋庸置疑的。

我们亦可这样理解,皮亚诺公理规定,任何一个自然数 \(i\) 有且仅有一个后继 \(i'\),既然 \(P(i)\Rightarrow P(i')\),且证得 \(P(s)\) 成立,则从 \(P(s)\) 可推得任意在区间内的 \(x\),命题 \(P\) 都成立。

Claim. 对于任意的自然数 \(n\),都有 \(1+2+3+4+\dots +n=\dfrac{n(n+1)}{2}\).

Proof.

最小的自然数为 \(1\),显然当 \(n=1\) 时命题成立.

设当 \(n=k(k\in \operatorname{\N})\) 时,命题成立,即得 \(1+2+3+4+\dots k=\dfrac{k(k+1)}{2}\),下面证明当 \(n=k+1\) 时命题成立,即证 \(1+2+3+4+\dots k+1=\dfrac{(k+1)(k+2)}{2}\).

\[1+2+3+4+\dots +k+1=1+2+3+4+\dots +k+k+1=\dfrac{k(k+1)}{2}+k+1=\dfrac{k(k+1)+2(k+1)}{2}=\dfrac{(k+1)(k+2)}{2} \]

命题成立.

即对于任意的自然数 \(n\),都有 \(1+2+3+4+\dots +n=\dfrac{n(n+1)}{2}\).

应用归纳法证明经典贪心算法

Kruskal 最小生成树算法

算法流程. 将所有候选边按照边权升序排序,依次决策。若加入该边,图上无环,则加入。否则跳过。当加入 \(n-1\) 条边后决策结束。时间复杂度 \(O(m\log m)\),其中 \(m\) 为边数。

一般的,我们认为,Kruskal 的时间复杂度只与边数 \(m\) 有关。这是一个和自然数有关的命题,考虑使用数学归纳法证明之。

Proof.

  • (归纳奠基)当 \(m=1\) 时显然成立.

  • (归纳递推)设当 \(m=k\) 时,执行 Kruskal 算法所取的边集为 \(T'\),还未选择的,且加入 \(T'\) 后不会形成环的,边权最小的边为 \(e\)。当 \(m=k+1\) 时,最小生成树为 \(T\)。下面证明 \(T=T'+e\)

    • \(e\in T\),显然成立.
    • \(e\notin T\),将 \(e\) 放入边集 \(T\) 中,会形成环。环上必定存在至少一条边 \(f\) 不在 \(T'\) 中,我们分析 \(f\)\(e\) 的关系.
      • \(f<e\),执行 kruskal 算法时,\(f\) 应当在 \(e\) 之前被选。但 \(f\) 不应当在 \(T'\) 中(否则当前会形成环),矛盾.
      • \(f>e\),此时,我们通过舍弃 \(f\),使用 \(e\) 可获得比原本 MST 更小的生成树。矛盾.

    综上所述,\(f=e\)。命题 \(e\notin T\) 不成立.

综上,我们证明了执行 Kruskal 算法,每一步添加的边必定在 MST 中.

基于贪心思想的区间覆盖问题

区间完全覆盖问题

\(n\) 条线段,第 \(i\) 条线段覆盖区间 \([l,r]\),求至少需要多少条线段,覆盖区间 \([1,m]\)

这是一个经典问题。我们采用贪心的策略解决,具体如下。

Greedy select.\(pos\) 为当前已覆盖区间右端点,找到一条左端点 \(\le pos+1\),右端点最大的线段,并更新 \(pos\)

Proof. 该策略每次选择的为合法的,右端点最大的线段。每次的选择能够将 \(pos\) 扩展到最大,即将已覆盖区间扩展到最大。

我们亦可从全局最优性证明。由于每次选择区间必须合法,显然,\(pos\) 越大,可选择的区间越多。故该贪心策略必定导致答案更优不劣。

Example.

namespace solution
{#define x first#define y secondtypedef pair<int,int> PII;vector <PII> line;int n,m;void solve(){cin>>n>>m;for(int i=1;i<=n;i++){int l,r;cin>>l>>r;line.push_back(PII(l,r));}sort(line.begin(),line.end());int pos = 1,tot = 0,ans = 0;while(pos < m){int maxn = 0;for(int i = tot;i < line.size();i++) {if(line[i].x <= pos + 1) maxn = max(maxn,line[i].y);else {tot = i;break;} }pos = maxn;ans ++;}cout<<ans<<"\n";return;}   
}

给定 \(k\) 条线段,询问 \(q\) 次,每次询问最少用多少条线段覆盖区间 \([l,r]\)

一个比较显然的策略是,对于每次询问,令 \(pos=l\) 执行上述 Greedy select。但复杂度并不理想。

Claim.\(i\) 经线段 \([i,j]\) 跳到 \(j\) 后,执行 Greedy select 选择的线段和前面选择的必不重复。

Proof. 若从 \(j\) 贪心选择线段和前面重复,则前面可选择该线段以获得更大的 \(pos\)。矛盾。

因此,对于任意的 \(i\),执行 \(k\)Greedy select独立 的,不受前面选择影响。据此,我们预处理 \(f_{i,j}\) 表示 \(i\) 执行 \(2^j\)Greedy select 扩展到的 \(pos\)。对于每次询问,从 \(l\) 倍增跳即可。

Example.

for(int i=1,j=1,maxn = 0;i<=n*2;i++)
{while(j <= k && p[j].x <= i) {r = max(r,p[j].y+1);j++; }f[0][i] = r;
}
for(int i=1;i<=20;i++)for(int j=1;j<=n;j++) f[i][j] = f[i-1][f[i-1][j]];

给定一个长度为 \(n\) 的环,有 \(k\) 个区域被覆盖,求最少需要几条线段覆盖整个环。
Source:洛谷 P6902 [ICPC2014 WF] Surveillance

该问题和上面类似,只是将序列变为环。常见的处理思路是断环为链。具体的,将所有跨过 \(1\) 的线段 \([l,r]\) 视为 \([l,n+r]\)。一条线段 \([l,r]\) 跨过 \(1\) 当且仅当 \(l>r\)

Example. paste

最大不相交区间数问题

数轴上有 \(n\) 个区间 \([a_i,b_i]\),选择尽可能多的区间,使得区间两两不相交。

Greedy select. 将所有区间按照右端点排序,若右端点相同,按照左端点排序。顺序选模拟即可。

Proof / Analysis. 考虑反证法。假设贪心算法执行完毕后,扔掉其选择的一段区间 \([u,v]\),可选择另外若干条新区间 \([a_1,b_1],[a_2,b_2]\dots\) 且不会对其他已选择区间造成影响。分类讨论新区间与 \([u,v]\) 的关系。

  • \([a_i,b_i]\subseteq [u,v]\),这种情形可推广为 \(b_i\le v\)。根据贪心算法,不可能。

  • \(b_i\ge v\) 前面提到,不可能存在 \(b_i\le v\)。故交换二者不可能更优。

证毕.

带需求约束的最小集合覆盖问题

在一条数轴上,给定若干区间 \([l_i,r_i]\),每个区间都有一个约束 \(k_i\),要求区间内 至少\(k\) 个点被染色,求满足所有约束条件下,至少染色多少个点。
P1250 种树 P1986 元旦晚会 SP116 POI2015 KIN
CSP-S2024 超速检测

Greedy Select. 考虑将所有询问区间离线到数轴上。将所有区间按照右端点排序。若该区间已经满足条件,忽略。否则从右边开始,枚举没染色的点染色。我们一定尽可能希望给一个区间染色能影响下一个区间,故从右往左染色。实现时可使用树状数组加速。

Proof. 由于我们总是从每个区间的右端开始满足需求,对于每一个区间,我们在尽可能靠右的位置放置必要的点以满足它的需求。任何尝试减少点数的方法都会导致某些区间无法满足需求,因为去除任何点都会导致至少一个区间的覆盖需求不足。

Example.


void solve()
{n = read<int>(),m = read<int>();bit.clear();ask.clear();memset(tree,0,sizeof(tree));for(int i=1;i<=m;i++) {int a,b,c;a = read<int>(),b = read<int>(),c = read<int>();a ++,b ++;ask.push_back({a,b,c});}sort(ask.begin(),ask.end());int res = 0;for(auto [a,b,c]:ask){int tot = bit.query(b) - bit.query(a-1);if(tot >= c) continue;for(int i = b;i>=a;i--){if(tot >= c) break;if(!tree[i]) {tree[i] = 1;bit.update(i,1);tot ++,res ++;}}}print(res,'\n');return;
}

反悔贪心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/832487.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全局平衡二叉树 (GBST) 小记

全局平衡二叉树 (GBST) 小记 以下全局平衡二叉树简称 \(\text{GBST(Globel Balanced Search Tree)}\)。 我认识的大多数人,对 \(\text{GBST}\) 的理解基本上都是 静态 \(\text{LCT}\),或者静态 \(\text{Top Tree}\),不过我对 \(\text{LCT}\) 的理解可能还差一点,所以我不打…

考研打卡(15)

开局(15) 开始时间 2024-11-12 20:37:51 结束时间 2024-11-12 22:41:32现在在敷泥膜数据结构设已知一稀疏矩阵的三元组表为:(1,2,3),(1,6,1),(3,1,5),(3,2,-1) (5,4,5),(5,1,-3),则其转置矩阵的三元组表中的第三个三元组为____(山东大学2013年) A (2,…

25. 使用MySQL之使用触发器

1. 触发器 MySQL语句在需要时被执行,存储过程也是如此。但是,如果你想要某条语句(或某些语句)在事件发生时自动执行,怎么办呢?例如:每当增加一个顾客到某个数据库表时,都检查其电话号码格式是 否正确,州的缩写是否为大写;每当订购一个产品时,都从库存数量中减去订购…

Mysql表分区实操

创建分区表 create table user(id int(11) not null,name varchar(32) not null) partition by range(id) ( partition p0 values less than(10), partition p1 values less than(20), partition p2 values less than(30), partition p3 values less than maxvalue )分区表数据…

【Azure Bot Service】部署NodeJS ChatBot代码到App Service中无法自动启动

问题描述 把NodeJS的ChatBot代码部署到App Service环境中,通过VS Code直接部署,显示部署成功。但是通过URL访问时候,却是 :( Application Error 。问题解答 App Service遇见Application Error,第一步,查看日志。 发现启动时候遇见: 2024-11-12T12:22:40.366223350Z Error…

Chrome如何卸载掉插件?如此简单!

前言 我之前由于好奇,一下子装了很多的chrome插件,这些插件虽然都有很多功能,但是,由于我的电脑性能不佳,浏览器一下子装这么多插件,有点带不动了。所以,我就想到了卸载谷歌浏览器,哦不,是卸载浏览器里面的插件。今天,我就来介绍下,如何在chrome里面卸载一些不常用的…

rocky9如何重置root密码

rocky9如何重置root密码 第一步第二步 在箭头指的地方把 "ro" 改成 "rw"另一个箭头指的地方写上如下内容:init=/bin/bash如图所示第三步 Ctrl + x 按当前的配置启动第四步 输入命令root 用户密码直接输入以下内容passwd如果更改其他用户第五步 最为关键的…

多校A层冲刺NOIP2024模拟赛21

多校A层冲刺NOIP2024模拟赛21\(T1\) A. 送信卒 \(90pts/100pts\)部分分\(90pts\)设最后的可能的最短路中左右共移动了 \(d\) 次,上下共移动了 \(x\) 次。则等价于求 \(\min \{ x_{i}k+d_{i} \}=s\) 的解,观察到 \(d \in [0,\min(\left\lceil \frac{nm}{2} \right\rceil,s)]\)…

[论文阅读] ZePo: Zero-Shot Portrait Stylization with Faster Sampling

写在前面 原文:ZePo GitHub:Github ZePo 关键词:肖像风格化、扩散模型、零样本快速生成 阅读理由:对扩散模型的改进,可以实现零样本快速生成图像,学习一下思路以及实验设计 前置知识:LCM以及GithubLCM(找时间写一下),可参考LCM&CM,一致性蒸馏、图像质量评价速览…

2024.11.12 鲜花

P11270 【MX-S5-T4】魔法少女们 题解这世界那么多人 这世界有那么多人 人群里 敞着一扇门 我迷朦的眼睛里长存 初见你蓝色清晨 这世界有那么多人 多幸运 我有个我们 这悠长命运中的晨昏 常让我 望远方出神 灰树叶飘转在池塘 看飞机轰的一声去远乡 光阴的长廊 脚步声叫嚷 灯一亮…

Java代码实现行列转换

本代码想要达到的效果 测试完整代码如下(copy直接运行): public class TestConvert { public static void main(String[] args) { ArrayList<Attribute> sourceList = new ArrayList<>(); for (int i = 0; i < 3; i++) { for (i…

SS241112A. 定向越野(walk)

这道题目要求找到从起点出发遍历所有点并回到起点的最小路径长度,路径必须直角拐弯。文章证明了答案是有理数,并通过直观分析和大胆猜测得出初始方向必须是某一个向量的方向,最终使用状压 DP 解决问题,时间复杂度为 $O(n^4 2^n)$。SS241112A. 定向越野(walk) 题意 给你 \…