SRM146 DIV2 1000
贪心策略的题,令 \(n\) 表示未过桥的人数,\(a_i\) 表示每个人过桥的时间并按从小到大排序,\(sum\) 表示最少过桥总时间,策略如下:
若 \(n=1\),\(a_0\) 直接过桥,\(sum(1)=a_0\)
若 \(n=2\),\(a_0a_1\) 过桥,\(sum(2)=a_1\)
若 \(n=3\),\(a_0a_2\) 过桥 \(a_0\) 回来,\(a_0a_1\) 过桥,\(sum(3)=a_0+a_1+a_2\)
若 \(n \geq 4\),不断选择速度最慢的两个人 \(a_{n-1}, a_{n-2}\) 执行以下两种策略之一:
(1) \(a_0 \ a_{n-1}\) 过去 \(a_0\) 回来,\(a_0 \ a_{n-2}\) 过去 \(a_0\) 回来,耗时 \(s_1=a_{n-1}+a_0+a_{n-2}+a_0\)
(2) \(a_0 \ a_1\) 过去 \(a_0\) 回来,\(a_{n-2} \ a_{n-1}\) 过去 \(a_1\) 回来,耗时 \(s_2=a_1+a_0+a_{n-1}+a_1\)
每次选择耗时最少的那种策略,直到 \(n < 4\),\(sum=sum(n-2) + min(s1, s2)\)
下面证明该贪心策略最优
核心策略
每次总是选择桥对面那些人中速度最快的人回来
很显然,每次选择速度最快的人回来一定不会比选择其他人回来更差,因此核心策略是一种最优策略
引论1
在核心策略下,\(a_0\) 是最后过桥并且再也不回去的人
根据核心策略,若 \(a_0\) 在桥对面让 \(a_0\) 回来,因此 \(a_0\) 一定是最后一个过去
引论2
在核心策略下,回合结束后 \(a_1\) 要想留在桥对面当且仅当在该回合过桥的组合是 \(a_0a_1\)
一组人 \(xa_1\) 过桥,若 \(x \not = a_0\),则 \(a_0\) 不在桥对面,\(a_1\) 一定是桥对面耗时最小的那个,此时 \(a_1\) 一定会回来
\(n \leq 3\) 证明
每一轮都有 \(C_n^2\) 种组合选择人过去,再选择桥对面速度最快的那个人回来,因此:
最多只有 \(C_n^2C_{n-1}^2C_{n-2}^2...C_2^2\) 种过桥组合
对 \(n \leq 3\) 时枚举证明即可。
\(n \geq 4\) 证明
将两个人过去一个人回来定义为一个回合,从任意一种可能的过桥组合出发,通过以下步骤调整该过桥策略:
定义
\(round(xyz)=\) \(xy\) 过去 \(z\) 回来的回合(就是一般的回合),耗费 \(s=max(x,y) + z\)
\(stay(xyz)=\) \(xy\) 过去 \(z\) 回来,且\(x\) 再也不回来的回合,耗费 \(s=max(x,y) + z\)
\(stay_2(xyz)=\) \(xy\) 过去 \(z\) 回来且 \(xy\) 再也不回来的回合,耗费 \(s=max(x,y) + z\)
\(stay(x)=stay(xa_0a_0\)),耗费 \(s=x+a_0\)
\(stay(xy)=stay_2(xya_1\)),耗费 \(s=max(x,y)+a_1\)
\(instead=round(a_0a_1a_0)\),耗费 \(s=a_0+a_1\)
\(A→B\) 表示将回合 \(A\) 调整为 \(B\)(调整意味着改变该回合的过桥和回来的人)
步骤1
将所有的 \(stay(xyz)→stay(x)\)
引论1保证一定可以调整成功
步骤2
将所有的先后发生的两个回合 \(round(stp)(p \not = t)\) 和 \(stay_2(xyt)\) \(→\) \(instead\) 和 \(stay(xy)\),同时将 \(stay(xy)\) 插入到 \(instead\) 后面,将该 \(instead\) 重新用 \(instead_{xy}\) 表示
引论2保证一定可以调整成功
定义
定义 \(stay(x)\) 和 \(stay(xy)\) 为留住回合
定义 \(instead_{xy}\) 为 \(stay(xy)\) 的关联回合
步骤1和步骤2调整结束后,留住回合和关联回合组成了所有的回合,并且 \(instead_{xy}\) 紧邻 \(stay(xy)\)
步骤3
对任意两个在去掉关联回合下相邻的留住回合,有以下4种情况:
1. \(stay(x)\) 和 \(stay(y)\) (不妨令前面那个回合先发生,下同)
令 \(b_1=max\{x,y\}, b_0=min\{x,y\}\) 考虑以下2种方案:
a. \(stay(x)→stay(b_1), stay(y)→stay(b_0)\),调整后耗费 \(s_{1a}(b_1,b_0)=b_1+a_0+b_0+a_0\)
b. \(stay(x)→instead_{b_0b_1}, stay(y)→stay(b_0b_1)\),调整后耗费 \(s_{1b}(b_1)=a_0+a_1+b_1+a_1\)
选择耗费最少的方案,将情况1所述调整方案表示为:\(f(A,B,p,q)\),其中 \(A=stay(x), B=stay(y),p=b_1,q=b_0\)
原耗费 \(t_1=x+a_0+y+a_0\)
新耗费 \(s_1(b_1,b_0)=min(s_{1a}(b_1,b_0), s_{1b}(b_1))\)
易证 \(s_1 \leq t_1\)
2. \(stay(x)\) 和 \(stay(yz)\)
令 \((b_2, b_1, b_0)=(xyz按耗时从大到小排序)\),调整方案:
\(f(stay(x), instead_{yz}, b_2, b_1)\) 和 \(stay(yz)→stay(b_0)\)
原耗费 \(t_2=x+a_0+a_0+a_1+max(y,z)+a_1\)
新耗费 \(s_2=s_1(b_2,b_1)+b_0+a_0\)
易证 \(s_2 \leq t_2\)
3. \(stay(xy)\) 和 \(stay(z)\)
令 \((b_2, b_1, b_0)=(xyz按耗时从大到小排序)\),调整方案:
\(f(instead_{xy},stay(xy), b_2, b_1)\) 和 \(stay(z)→stay(b_0)\)
原耗费 \(t_3=a_0+a_1+max(x,y)+a_1+z+a_0\)
新耗费 \(s_3=s_1(b_2,b_1)+b_0+a_0\)
易证 \(s_3 \leq t_3\)
4. \(stay(xy)\) 和 \(stay(zp)\)
令 \((b_3, b_2, b_1, b_0)=(xyzp按耗时从大到小排序)\),调整方案:
\(f(instead_{xy}, stay(xy), b_3, b_2)\) 和 \(f(instead_{zp}, stay(zp), b_1, b_0)\)
原耗费 \(t_4=a_0+a_1+max(x,y)+a_1+a_0+a_1+max(z,p)+a_1\)
新耗费 \(s_4=s_1(b_3,b_2)+s_1(b_1,b_0)\)
易证 \(s_4 \leq t_4\)
终证
由于:
1. 任意一种策略通过调整直到再也不能变为止所形成的最终策略都相同,就是该贪心策略
2. 这里的每一步调整都会使得耗费不变或者更小
反证法可以证1,计算可以证2,因此该贪心策略就是最优策略,证毕。
1 class BridgeCrossing: 2 def minTime(self, a): 3 a = sorted(a) 4 i = len(a) - 1 5 s = 0 6 while i >= 3: 7 x = a[i] + a[0] + a[i-1] + a[0] 8 y = a[1] + a[0] + a[i] + a[1] 9 s += min(x, y) 10 i -= 2 11 12 if i == 2: 13 s += a[0] + a[1] + a[2] 14 elif i == 1: 15 s += a[1] 16 else: 17 s += a[0] 18 return s 19 20 # test 21 o = BridgeCrossing() 22 23 # test case 24 assert(o.minTime((1, 2, 5, 10 )) == 17) 25 assert(o.minTime((1, 2, 3, 4, 5)) == 16) 26 assert(o.minTime((100,)) == 100) 27 assert(o.minTime((1, 2, 3, 50, 99, 100)) == 162) 28
PS:在已知结论是对的情况下证明,尤其是这么不那么数学的证明,很难保证叙述中没有什么bug,更加重要的是即使啰嗦了这么多该种证明方式也不能称得上是严谨,完全严谨的证明方式恐怕需要将其全部转换为谓词表达式并利用计算机辅助推导,因此实际上这种证明根本没有什么意义。若想在比赛中确认这个贪心策略是最优策略只能依赖于个人的直觉和运气,有谁会去证啊?