贪心思想杂谈及证明方法归纳

贪心思想

贪心就是局部最优解变成了全局最优解。

下面介绍一种邻项交换的证明,以 P1080 [NOIP2012 提高组] 国王游戏 为例。
x,y(x<y) 是相邻的两个元素,那么很重要的一点是对于 [1,x)(y,n] 的其他元素,这两者之间的交换是不会改变其答案的。

考虑什么时候 xy 要交换。根据题意,我们要的是最大值最小,交换前的最大值为 max{i=1x1aibx,i=1y1aiby},交换后的最大值为 max{i=1x1aiby,ayi=1y1aiaxbx},当前者大于后者时,也就是交换后能使得最大值变得更小,那么我们就会选择交换,当然,前面的式子还可以进一步化简。

容易观察到:i=1x1aiby<i=1y1aiby

i=1x1aibx<ayi=1y1aiaxbx

所以将条件转化为 i=1y1aiby>ayi=1y1aiaxbx,即 axbx>ayby

由此可见,我们要将 aibi 升序排列(当前者大于后者时会交换)。

另一种想法是考虑贡献,下面以这题为例:
给出 n 个区间 [li,ri],求最少的点的个数使得点能覆盖到所有区间。

首先,题目中每个区间的贡献为 1,而且我必须把区间覆盖,假如现在已经有一个点在区间上,那么我完全可以跳过,倘若没有,那我一定会选出一个位置,既然答案一定会加一,那我就考虑怎么样才能让我选的这个点价值最大,即在之后还能覆盖到最多的点。

于是我们很自然的想到,选择右端点的数一定是最优的,先感性理解一下。

现在有三条线段,对于第一条,我应该在哪里选择呢?显然是右端点,这样更有希望够到后面的线段,因为右端点是递增的。

我们可以设取了点 xxri,那么对于 x 覆盖到的区间构成的集合记为 Sx,对于 r 覆盖到的区间构成的集合记为 Sr,因为前面的区间一定已经被覆盖了,所以不再考虑,观察后面的区间,因为右端点递增,所以发现 SxSr

【例题】CF1203F1 Complete the Projects (easy version)

对于这题,假如能完成所有项目,那么总贡献仍然为 n,每个项目贡献为 1。所以考虑贪心。

一个比较直观的想法是优先做 b>0 的项目,因为这相当于是在“回血”,如果连某个 b>0 的项目都无法完成,那么就更别指望后面 b<0 的项目带来的贡献了,这一部分正确性显然。

那么对于 b<0 的又是什么策略呢?你可能会想按照 b 降序排列,但很容易找出反例。

n = 2 , r = 1000
1 -1
1000 -2

具体的我们根据相邻的两个项来求这个东西。设 x,y(x<y) 是相邻的两个元素,那么交换前能满足条件,即 r>axr+bx>ay,整理得 r>max(ax,aybx)
对于交换后同理可得 r>max(ay,axby)。什么时候更优呢?显然是限制更宽的时候,也即 max(ax,aybx)>max(ay,axby) 时,需要交换。

这里我们发现和上文国王游戏一样优秀的性质(注意此时 b<0):
ax<axby
ay<aybx

所以上式可以化简为 aybx>axby,即 ax+bx<ay+by,所以可知需要按照和降序排序。

这里简单提一下如何化简的。首先假如 max(ax,aybx)>max(ay,axby) 左边的 ax 作为最大值出现,那么右边的 axby 无论如何比它大,不符合题意,右边 ay 作为最大值时答案显然,无需比较所以选 axby 作为最大值。

换一个角度,也可以考虑那个下界要求更高,因为做了一个任务后能力值一定减少了,所以后面的那个显然要求更高。

上面说的都有一个共性,也就是总贡献是一定的,反正都要完成。那假如改为求最大贡献值呢?

参考本题的 hard version。我们使用 DP 在原有基础上进行选择即可,另一种可行的方法是使用反悔贪心,但是本文不做详细展开。

关于这种方法,还可以参考 P4823 [TJOI2013] 拯救小矮人。

首先感性的分析,这也是贪心很重要的一环,我们肯定是让“逃生能力”弱的小矮人先行离开,否则可能就再也没有离开的机会了!这里的“逃生能力”定义为 ai+bi。另一种看法是肯定要让个子高的在下面,手长的在上面,那么哪个才是正确的呢?

考虑邻项交换,定义如下。

在这里为了直观一点,假设两个人都在最前面,并且都能逃脱(注意这只是假设)。

交换前

{A+ax+ay+bxHA+ay+byH

交换后

{A+ax+ay+byHA+ax+bxH

整理可得 ax+bx>ay+by 时需要交换,那么升序排列即可。但是到这还没完,因为我们求出的只是所有人走完的最优方案,但是不一定走完,所以套一个背包来求最大值。

P1248,P1561,P2123:这三题属于同一个问题,让我们来看看。

首先从最直观的 P2123 开始,容易想到用邻项交换解决问题,设 x,y(x<y) 是相邻的两个元素,一个显然的事情是 y 对应的值一定是大于 x 对应的值的,这提供了很大方便。

具体的,在交换前设 s=i=1x1ax,则 y 的值为 max(max(cx1,s+ax)+bx,s+ax+ay)+by,交换后,x 的值变成 max(max(cx1,s+ay)+by,s+ax+ay)+bx。外面的加号可以算进去,那么前者就变成了 max(cx1+bx+by,s+ax+bx+by,s+ax+ay+by),后者变成了 max(cx1+bx+by,s+ay+by+bx,s+ax+ay+bx)。注意到第一项是一样的,那么不管。

max(s+ax+bx+by,s+ax+ay+by)>max(s+ay+by+bx,s+ax+ay+bx)

同时减去 s+ax+ay+bx+by 得:

min(bx,ay)<min(ax,by)

这个式子,似乎和之前都不一样,感觉不能进一步化简了,不妨就这么交上去,你会发现,过了,但是,真的是对的吗?接下来给出简单过程,详细请参考【ouuan】浅谈邻项交换排序的应用以及需要注意的问题

首先我们考虑邻项交换的本质是什么,简单地说可以看作是一个排序的策略,使得答案最优,那么排序策略需要一些重要的性质,详见这篇博客。而我们求出的这个东西却不满足 STL 需要的性质,所以是不能用来排序的。

于是我们尝试在原有基础上增加点什么,一个很容易想到的尝试是把 min(bx,ay)<min(ax,by) 找出来,改为 a 的升序排列,这样也很符合直观,也就是让 ni=1 更小。同样也可证明这样做是符合要求的,具体详见上文给出的博客。另一种可行的想法是分类讨论,这样也许你就能得到另一种可行的做法,也就是按照 a,b 的大小关系进行排序。

接着看一下另外两题为什么和这题是一样的,A,B 车间用时就是 ai,bi,c 就是最长用时,另一题略。

当然,前文所讲都是序列上的贪心,实际上贪心很多时候不会单独出现,而是会结合其他结构一起出现,有时候单纯只是一种思想。

【例题】P4574 [CQOI2013] 二进制A+B

本题可以使用数位 DP 解决,但是代码较为繁琐。但是假如使用贪心解答,代码就简单很多,但是思维量却加大了,所以我们需要做一个权衡。

int B(int x){return __builtin_popcountll(x);}
int C(int x){int r=0;while(x)r++,x/=2;return r;}
void solve(){
    cin>>a>>b>>c;mx=max({C(a),C(b),C(c)});
    a=B(a),b=B(b),c=B(c);if(a<b)swap(a,b);
    if(a+b<c) {cout<<"-1";return;}
    if(c==1) ans=1ll<<(a+b-1);
    else if(c<b) ans=(1ll<<(a+b-c))+((1ll<<(c-1))-1)*2;
    else if(c==b) ans=(1ll<<a)-1+(1ll<<b)-1;
    else if(c<=a) ans=(1ll<<a)+(1ll<<c)-1-(1ll<<(c-b));
    else if(c<a+b) ans=(1ll<<c+1)-1-(1ll<<(2*c-a-b));
    else if(c==a+b) ans=(1ll<<(a+b))-1;
    else ans=-1;  if(C(ans)>mx)ans=-1;
    cout<<ans<<endl;
}

【例题】P3294 [SCOI2016] 背单词

这题就不是简单的贪心了,首先使用 Trie 树把所有字符串倒序存起来,方便表示后缀。

然后贪心的考虑三个操作,首先发现第一个操作代价太大,显然超过了后面的总和,必然不选。这一想法就为字符串序列提供了一个隐性的条件,也就是一个单词的所有后缀都必须出现在这个单词前面。

然后分析一下二三两个操作,我们发现二其实就是三在找不到前缀时的答案,那么就可以只考虑三操作。显然,我们希望后缀中最大的那一个离当前字符串尽可能近。此时我们重构一下 Trie 树,只保留关键节点,也就是让子树大小变成字符串数量,因为我们要尽可能近,所以每个父节点的子树一定是按照大小从小到大排列的。

最后就可以考虑计算答案了,按照 dfn 序记录下前面出现过的个数和自己的位置即可。

【例题】P3243 [HNOI2015] 菜肴制作

这题非常好,首先明确题目要求是最小的能先就先,不是字典序最小(4 1 2 3 优于 2 3 4 1)。

那么我们这样贪心,显然,假如能把最大的 4 放在末尾,那么一定是最好的,因为理想情况就是 1 2 3 4,那么我们可以反向建边,这样问题就转化为了字典序问题,可以直接拓扑排序(每次优先取最大点)。

此外,我觉得当题目中出现类似“Note that you don't have to maximise it.”(不需要求最大化)的时候可以选择想一下贪心。

作者:紊莫

出处:https://www.cnblogs.com/wenmoor/p/17980917

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   紊莫  阅读(64)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题