邻项交换排序类贪心
2022/7/3 23:26:44
本文主要是介绍邻项交换排序类贪心,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
原理论述部分引用自浅谈邻项交换排序的应用以及需要注意的问题
luogu题单
引言
邻项交换排序是一种常见的贪心算法,通过比较两个相邻元素交换前后的优劣对整个序列进行排序,从而使得这个序列成为题目所求的最优解。
然而,邻项交换排序的应用有一些需要注意的地方,稍有不慎便会成为一个错误的算法。
简而言之,要求满足排序之后的得到的新排列唯一,或所有新排列均为最优解。
不然可能会出现求出局部而非全局最优解的情况:
如上图所示,假定这个空间为所有可能排列构成的一个排列空间,一个点对应一个排列。
再令圆上的所有排列恰巧比相邻的排列都更差。如此排序过程中排列S'就不会达到圆圈外的排列S的状态,因为任何到达S的排序方式上都会经过圆圈,即变得更差。
故S'只会在圈内寻找最优解。再假定S为最优解,则排序方式就显然错误了。具体的例子可以看皇后游戏的反例。
国王游戏
NOIP2012提高组D1T2 国王游戏
分析
考虑队伍中的两位相邻的大臣i,j(此时j在i后一位),分别用\(a_i,a_j,b_i,b_j\)表示这两位大臣左手上和右手上的数字,设这两位大臣前面的所有大臣左手上的数乘积为\(k\)。
两位大臣换序与否对前面和后面大臣均无影响,单独计算他们两人交换位置前后对答案的贡献。
i在j前面:\(max(\lfloor \frac{k}{b_i} \rfloor,\lfloor \frac{ka_i}{b_j} \rfloor)\)
j在i前面:\(max(\lfloor \frac{k}{b_j} \rfloor,\lfloor \frac{ka_j}{b_i} \rfloor)\)
当\(max(\lfloor \frac{k}{b_i} \rfloor,\lfloor \frac{ka_i}{b_j} \rfloor) <= max(\lfloor \frac{k}{b_j} \rfloor,\lfloor \frac{ka_j}{b_i} \rfloor)\)时,两者位置达到最优
\(\lfloor x \rfloor\)具有单调性,故可以提出到\(max\)函数外,并得到充分不必要条件\(max(\frac{k}{b_i},\frac{ka_i}{b_j})<=max(\frac{k}{b_j},\frac{ka_j}{b_i})\)
又因为\(\frac{ka_i}{b_j}>=\frac{k}{b_j},\frac{ka_j}{b_i}>=\frac{k}{b_i}\),可以简化条件为\(a_ib_i<=a_jb_j\)。利用不等式传递性,可知i,j可以不相邻。
由以上条件对原二元关系组进行排序,得到的排列若相邻均用<连接则排列方式唯一,即无论输入时顺序,都会得到唯一答案;若其中用部分等号连接,则等号两端\(a_ib_i==a_jb_j\),两两之间无具体顺序要求,均为最优。
(原题除了贪心,在计算答案时还需用高精度)
皇后游戏
洛谷P2123 皇后游戏
故技重施
用和国王游戏类似的方法,
这里我们先假设可以从邻项推广到不相邻两项(实际是不可以的),如此可以得到一份能在洛谷ac的错误代码
#include <iostream> #include <algorithm> using namespace std; const int N=20010; struct Node { int a,b; bool operator<(Node& y) { return min(a,y.b)<min(b,y.a); } } dc[N]; long long ans,sum,t,n; int main() { cin>>t; while (t--) { cin>>n; for (int i=1;i<=n;++i) { cin>>dc[i].a>>dc[i].b; } sort(dc+1,dc+n+1); ans=sum=0; for (int i=1;i<=n;++i) { sum+=dc[i].a; ans=max(ans,sum)+dc[i].b; } cout<<ans<<endl; } return 0; }
但这会被以下数据hack
2 4 1 1 1 1 3 5 2 7 4 1 1 3 5 1 1 2 7
输出为
16 17
输出两组输入的最终排序分别为
1 1 1 1 2 7 3 5
1 1 3 5 1 1 2 7
此时,排序后的排列方式不唯一,且两排列的最终答案不等价。
观察发现第二组数据23和34满足对相邻元素的比较,但是24两元素的比较结果是不符合“比较”的要求。
这是因为在这个式子,新定义的比较运算符不符合严格弱序,即满足了<的传递性(这个稍后再证明),但不满足=的传递性。使得等号在234的传递中出现了错误。
严格弱序
定义:对一个比较运算符<<(若不满足运算符则为!<<),若其为严格弱序,则要求有以下性质:
1.x << x(非自反性)
2.若x << y,则y !<< x(非对称性)
3.若x << y, y << z,则x << z(传递性)
4.若x!<<y,y!<<z,z!<<y,y!<<z,则x!<<z,z!<<x(不可比性的传递性)
[如果觉得“不具有不可比性的传递性”不好理解的话,有个简单的例子:P(x,y)=x+1<y。这个偏序关系满足非自反性(x+1>x)、非对称性(x+1<y⇒y+1>x)和传递性(x+2<y+1<z),但不具有不可比性的传递性(1+1=2,2+1=3,1+1<3)]
满足严格弱序,则满足<=符号的传递性,即等号连接的元素两两换序而结果不变。
这非常重要,就像引言里提到的那样:如果最后的多种排列中存在不等价情况,就会导致答案错误。
皇后游戏后续
完善解法
问题在于\(min(a_i,b_j)=min(a_j,b_i)\)的时候。因为\(a_n\)的前缀和对答案有负影响,若此时将\(a_i\)较小的元素放在前面则可以解决问题。
[其实大多不能不满足严格弱序的排序,都可以通过进一步分类讨论的方式来消除等号带来的乱序]
#include <iostream> #include <algorithm> using namespace std; const int N=20010; struct Node { int a,b; bool operator<(Node& y) { return min(a,y.b)==min(b,y.a)?a<y.a:min(a,y.b)<min(b,y.a); } } dc[N]; long long ans,sum,t,n; int main() { cin>>t; while (t--) { cin>>n; for (int i=1;i<=n;++i) { cin>>dc[i].a>>dc[i].b; } sort(dc+1,dc+n+1); ans=sum=0; for (int i=1;i<=n;++i) { sum+=dc[i].a; ans=max(ans,sum)+dc[i].b; } cout<<ans<<endl; } return 0; }
引文的末尾还有一个基于小数据的排序检测器,感兴趣的可以跳转阅读。
生产加工调度
洛谷P1248
这题是有贪心结论johnson准则的,可以利用文章中提供的结论证明两台机器时,johnson准则的正确性。
思路看第一篇题解即可。
这篇关于邻项交换排序类贪心的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南