ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

邻项交换排序类贪心

2022-07-03 21:06:31  阅读:145  来源: 互联网

标签:lfloor 排列 frac int 邻项 dc 排序 贪心


原理论述部分引用自浅谈邻项交换排序的应用以及需要注意的问题
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准则的正确性。
思路看第一篇题解即可。

标签:lfloor,排列,frac,int,邻项,dc,排序,贪心
来源: https://www.cnblogs.com/xyc1719/p/16440880.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有