相邻项交换排序类贪心问题

事实上这种考虑方式完全可以构成一类题的解题思路,因此我决定做一个小结,不过无法像ouuan奆佬那样写出完整的论文.

重要参考:ouuan奆佬的博客浅谈邻项交换排序的应用以及需要注意的问题

例题1 国王游戏

题目描述

恰逢\(H\)国国庆,国王邀请\(n\)位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这\(n\)位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入格式

第一行包含一个整数\(n\),表示大臣的人数。

第二行包含两个整数\(a\)\(b\),之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来\(n\)行,每行包含两个整数\(a\)\(b\),之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出格式

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

解析

此题是我见过的第一个邻项交换考虑最优性的题目,一句话来讲,对于一个大臣\(i\),他的得分\(c_i\)这样定义:
\[ c_i=\lfloor \frac{\prod_{j=1}^{i}l_j}{r_i}\rfloor \]
我们要最小化\(c\)的最大值.这时候我们考虑两个大臣\(a,b\),如果\(a\)排在\(b\)的前面,就有\(ans1=max(\frac{L}{r_a},\frac{L\times l_a}{r_b})\),而如果\(b\)排在\(a\)前面,就有\(ans2=max(\frac{L}{r_b},\frac{L\times l_b}{r_a})\),其中\(L\)代表国王左手的数.我们假设不交换更优,即\(ans1<ans2\),那就意味着\(max(\frac{L}{r_a},\frac{L\times l_a}{r_b})<max(\frac{L}{r_b},\frac{L\times l_b}{r_a})\).为了表示简便,我们令\(k1=\frac{L}{r_a},k2=\frac{L\times l_a}{r_b},k3=\frac{L}{r_b},k4=\frac{L\times l_b}{r_a}\),这时候我们发现一定有\(k1<k4,k2>k3\),那么要想让\(ans1<ans2\)成立,则只需要让\(k4>k2\),就可以保证\(k4\)是四个数中最大的了,写出来就是
\[ \frac{L\times l_b}{r_a}>\frac{L\times l_a}{r_b} \]
整理可得\(l_ar_a < l_br_b\),这就是我们排序的条件.

例题2 皇后游戏

题目描述

皇后有 \(n\) 位大臣,每位大臣的左右手上面分别写上了一个正整数。恰逢国庆节来临,皇后决定为 \(n\) 位大臣颁发奖金,其中第 \(i\) 位大臣所获得的奖金数目为第\(i-1\) 位大臣所获得奖金数目与前 \(i\) 位大臣左手上的数的和的较大值再加上第 \(i\) 位大臣右手上的数。

形式化地讲:我们设第 \(i\) 位大臣左手上的正整数为 \(a_i\),右手上的正整数为 \(b_i\),则第 \(i\) 位大臣获得的奖金数目为 \(c_i\)可以表达为:
\[ c_i= \begin{cases} &a_1+b_1& &{i=1}\\ &max(c_{i-1},\sum_{j=1}^{i}a_j)+b_i& &{2\le i \le n}\\ \end{cases} \]
当然,吝啬的皇后并不希望太多的奖金被发给大臣,所以她想请你来重新安排一下队伍的顺序,使得获得奖金最多的大臣,所获奖金数目尽可能的少。

注意:重新安排队伍并不意味着一定要打乱顺序,我们允许不改变任何一位大臣的位置。

输入格式

第一行包含一个正整数 \(T\),表示测试数据的组数。

接下来 \(T\) 个部分,每个部分的第一行包含一个正整数 \(n\),表示大臣的数目.

每个部分接下来 \(n\) 行中,每行两个正整数,分别为 \(a_i\)\(b_i\),含义如上文所述。

输出格式

\(T\) 行,每行包含一个整数,表示获得奖金最多的大臣所获得的奖金数目。

解析

这道题看起来和国王游戏非常相似,因此我们也考虑使用邻项交换法.经过各种我不会的化简,我们得到的条件应该是\(min(a_i,b_j)<min(a_j,b_i)\),看起来非常正确,甚至在洛咕真的能\(A\)

但是

它是错的

因为我们得出的条件并不能满足严格弱序.\(C++\)要求排序运算符必须满足严格弱序:

(以下内容摘自ouuan奆佬的博客)

  1. \(x≮x\) (非自反性)
  2. \(x<y\),则 \(y≮x\) (非对称性)
  3. \(x<y,y<z\),则 \(x<z\) (传递性)
  4. \(x≮y,y≮x,y≮z,z≮y\),则 \(x≮z,z≮x\) (不可比性的传递性)

事实上我们得出的结论正是不满足第四条,也就是说,当\(min(a_i,b_j)=min(a_j,b_i)\)时,按照我们的条件理应交换\(a,b\),但是交换之后我们并不能保证最后产生的解更优,甚至可能更劣.我们要对相等的情况进行单独处理.考虑对于全局来说,\(a\)的前缀和能够对答案产生影响,因此为了最小化最大值,我们应该将\(a\)较小的放在前面,我们应用这种判断方式当且仅当\(min(a_i,b_j)=min(a_j,b_i)\).

inline bool cmp(node a, node b) {
    return min(a.l, b.r) == min(a.r, b.l) ? a.l < b.l : min(a.l, b.r) < min(a.r, b.l);
}

这才是这道题的最终做法.

总结

在可以通过比较相邻两项得出交换或不交换一定不会更差时,可以通过邻项交换排序的方式来得到最优解。

邻项交换排序的比较函数需要满足严格弱序,并且排序完成后任意交换相邻元素都不会更优。

使用这种算法时,一定要注意以上两点,才能得到真正正确的算法。

(以上内容摘自ouuan奆佬的博客%%%)

猜你喜欢

转载自www.cnblogs.com/i-cookie/p/11429984.html
今日推荐