5 个海盗分 100 枚金币
题
5 个海盗 A > B > C > D > E(按资历排序)分 100 枚金币。规则:
- 最资深的活人提出分配方案,全体(含提议者)投票;
- 赞成票至少占一半(含提议者本人)则通过,按方案分;否则提议者被扔下海,由下一位资深者重新提议。
每个海盗都绝对理性,偏好依次为:先保命,再多拿金币,两者都一样时宁愿把提议者扔下海。金币不可分割。A 该怎么提?
一句话
A 拿 98,C 和 E 各拿 1,B 和 D 拿 0——方案里没有一处是"公平",全靠逆向归纳:每个人只比较"这个方案"和"提议者死后我能拿多少"。
关键技巧
从最小的局倒着推。 每个人投票时拿当前方案和"提议者被扔下海后的那局"比。所以只要知道 \(k-1\) 人局的结果,就能算出 \(k\) 人局:
- 在 \(k-1\) 人局里拿 \(x\) 枚的人,现在给他 \(x+1\) 枚就能买到他的票(给 \(x\) 枚他会投反对——无差别时宁愿杀人)。
- 提议者要凑够票数,就去买最便宜的票:那些在下一局里一无所获的人,每票 1 枚。
这是一种博弈里的逆向归纳(backward induction),和秘书问题、掷骰子重投这类最优停止问题同一个思路:当下的决策只取决于"之后会怎样"的精确值。
解
记"剩几个人"为局面。需要的赞成票为 \(\lceil k/2 \rceil\)(含提议者)。
| 局面 | 提议者 | 需要票数 | 方案(按资历从高到低) |
|---|---|---|---|
| D, E | D | 1 | D 100, E 0 |
| C, D, E | C | 2 | C 99, D 0, E 1 |
| B, C, D, E | B | 2 | B 99, C 0, D 1, E 0 |
| A, B, C, D, E | A | 3 | A 98, B 0, C 1, D 0, E 1 |
逐行看:
- 2 人:D 自己一票就是一半,直接全拿。E 无能为力。
- 3 人:C 需要 2 票。D 在下一局拿 100,买不起;E 在下一局拿 0,给 1 枚就买到。
- 4 人:B 需要 2 票。下一局里 D 拿 0,给 D 1 枚。
- 5 人:A 需要 3 票,自己一票,还要买 2 票。下一局里拿 0 的是 C 和 E,各给 1 枚。
每一步要买的票数恰好等于下一局的"零收入者"人数,所以每一局的方案都是唯一的。
延伸
- 规则改成严格过半:2 人局时 D 需要 2 票,E 一定反对,D 必死——于是在 3 人局里 D 会无条件支持 C,C 拿 100。往上推:4 人局 B 需 3 票,买 D、E 各 1 枚,方案 \(98, 0, 1, 1\);5 人局 A 需 3 票,买 C(1 枚)再加 D 或 E 之一(各要 2 枚),方案 \(97, 0, 1, 2, 0\) 或 \(97, 0, 1, 0, 2\),不再唯一。可见"至少一半"还是"严格过半"一字之差,答案整个变了。
- 海盗更多时(至少一半规则):\(n\) 人局的提议者拿 \(100 - \lfloor (n-1)/2 \rfloor\),给与自己同奇偶编号的其余人各 1 枚。\(n = 199\)、\(200\) 时提议者只剩 1 枚;\(n = 201\) 时 100 枚全得送出去,自己分文不取也能活。
-
超过 201 人(Stewart, 1999):金币不够买票了,关键变成"必死者的票"。\(n=203\) 的提议者只有自己一票加 100 枚买来的票,差一票,必死;\(n = 204\) 时 203 号为了不死会无条件支持他,刚好够票。继续推,能活的提议者恰好是
\[ n = 200 + 2^k: \quad 201, 202, 204, 208, 216, 232, 264, 328, 456, \ldots \]两个幸存编号之间的那些人全部必死——他们的票是下一个幸存者的"免费票",间隔每次翻倍。 - 常见错误一:以为 A 要讨好 B。B 在下一局拿 99,买他的票最贵;该买的是下一局里最穷的人。 - 常见错误二:忘了写"无差别时投反对"。若无差别时投赞成,每个人收 0 枚也肯投票,逐级方案都会变——题目的偏好顺序必须说全。
数值验证
逆向归纳直接编程:每局让提议者按价格从低到高收买票,检查唯一性,再扫描到 600 人找幸存编号。
import math
def solve(n, G, need):
"""海盗编号 1..m,m 最资深、先提议。need(m) = m 人局通过所需票数(含提议者)。
偏好:先保命,再多拿金币,无差别时投反对。plan[m] = 分配 dict,None 表示提议者必死。"""
plan = {1: {1: G}}
for m in range(2, n + 1):
k = m - 1
while plan[k] is None: # m 死后,往下第一个能活的局面
k -= 1
base = plan[k]
votes = 1 + (m - 1) - k # 自己 + 夹在中间的必死者(白给的票)
short = need(m) - votes
if short <= 0:
plan[m] = {m: G}; continue
prices = sorted((base.get(i, 0) + 1, i) for i in range(1, k + 1))
cost = sum(p for p, _ in prices[:short])
if cost > G:
plan[m] = None; continue
alloc = {i: p for p, i in prices[:short]}
alloc[m] = G - cost
alloc['_unique'] = short == len(prices) or prices[short - 1][0] < prices[short][0]
plan[m] = alloc
return plan
half = lambda m: math.ceil(m / 2) # 至少一半(含提议者)
strict = lambda m: m // 2 + 1 # 严格过半
for rule, f in (("至少一半", half), ("严格过半", strict)):
p = solve(5, 100, f)
for m in range(2, 6):
a = p[m]
if a is None:
print(rule, m, "提议者必死"); continue
print(rule, m, [a.get(i, 0) for i in range(m, 0, -1)], "唯一" if a.get('_unique', True) else "不唯一")
# 至少一半 2 [100, 0] 唯一
# 至少一半 3 [99, 0, 1] 唯一
# 至少一半 4 [99, 0, 1, 0] 唯一
# 至少一半 5 [98, 0, 1, 0, 1] 唯一
# 严格过半 2 提议者必死
# 严格过半 3 [100, 0, 0] 唯一
# 严格过半 4 [98, 0, 1, 1] 唯一
# 严格过半 5 [97, 0, 1, 0, 2] 不唯一
p = solve(600, 100, half)
assert all(p[m][m] == 100 - (m - 1) // 2 for m in range(1, 202))
for m in range(2, 201): # 方案唯一,且恰好给同奇偶的人各 1 枚
a = p[m]
assert a.get('_unique', True)
assert sorted(i for i in a if i not in ('_unique', m) and a[i] > 0) == \
[i for i in range(1, m) if i % 2 == m % 2]
print([p[m][m] for m in (5, 10, 50, 199, 200, 201, 202)]) # [98, 96, 76, 1, 1, 0, 0]
print([m for m in range(201, 601) if p[m] is not None])
# [201, 202, 204, 208, 216, 232, 264, 328, 456]
200 人以上的部分,代码按一个确定的方案往下推,只用来数票数够不够。这一段谁拿金币不再唯一——提议者可以在多个同价候选人里任选 100 人——Stewart 原文专门处理了这种不确定性,结论同样是幸存编号 \(200 + 2^k\)。