百日囚徒
题目描述
监狱中关着100名囚犯,每人在一个独立的房间里,且无法用任何方式相互通信;每天会有随机一人被选出来放风,放风的地方有一盏灯,囚犯可以打开或关闭它,其他没有任何人会去动这个灯,其他囚犯不知道是谁被选中放风,也无法看到灯,假定最初灯就是关闭的。
某一天国王召集所有囚犯开了一个会,向他们说:如果某一天有人说所有的囚犯都已经放过风了,且情况属实,则所有人都会被释放,如果说错了则所有人都要被处死,然后给了囚犯20分钟讨论时间。
不考虑灯损坏或犯人意外死亡的理想情况下,他们能找出办法让所有人都被释放吗?
题目思路(低级版本)
很明显我们需要通过灯的状态变化次数来判断有多少人放过风。
从这100人中选出1个人,这个人只能关灯,并且他记下自己关灯的次数。其他99人出来放风的时候只能开灯,并且每人只能开一次灯(也就是说,如
果他以前已经开过灯了,就算他再次出来的时候灯是关着的也不能开)。当关灯的发现他已经关灯99次时,就可以宣称所有人都出来过了。
这个方法和穷举法相比差不多,这里就不做代码解释和思路分析了。
题目思路(进阶版本)
确认身份
前100天利用灯来互相确认自己的身份(灯初始状态为关闭,最好的情况是100天)。
-
计数人
首次放风的人不进行任何操作,设第一个二次放风的人为计数人,计数人把灯打开(比如第20天该囚犯第二次去放风,且前面没有人去动过灯,则说明连同他共有 19个人是首次放风,因为第20天的话,这个人去了两次,所以要减去一次),则此时生成一个该天数的计数(N-1) -
已知囚徒
设剩下的99人中,见到过灯灭状态的人为已知囚徒,因为计数人能通过开灯的日期准确推算出之前的人数 -
未知囚徒
设从未见过灯灭状态的人为未知囚徒,因为灯打开后,计数人无法获取放风的准确人数
这里有个最理想状态:
第101天,某首次放风囚徒发现灯为关闭状态,说明前100天均无人2次放风,即每人都是首次放风,可以宣布获救。
添加后续人员
确认完身份后,开始将未知囚徒一个一个变成已知囚徒(计数人通过灯的变化)
- 已知囚徒放风
不进行任何操作 - 未知囚徒放风
若灯为打开状态,将灯关闭,该未知囚徒变为已知囚徒。
若灯为关闭状态,不进行任何操作。 - 计数人放风
若灯为关闭状态,说明有一个未知囚徒变成了已经囚徒,计数N+1,并将灯打开,以便下一个未知囚徒传递信息
若灯为打开状态,说明暂时没有未知囚徒变成了已经囚徒,不进行任何操作。
当计数人放风时恰好N=100,则他可以宣布确定所有囚徒都放过风了。
代码实现
import random
days = 1 # 天数
light = False # 灯
times = [0 for i in range(0, 100)] # 每个囚徒放风次数的统计数组
counter = -1 # 计数人
know_list = [] # 已知囚徒
know_num = 0
while 1 == 1:
# 随机抽选一人放风
P = random.randint(0, 99)
# 前100天确认身份并且确认计数人
if days <= 101 and counter < 0:
times[P] += 1 # 灯灭放风有效
if times[P] == 1: # 第一次放风 什么也不用做
know_list.append(P) # 已确认身份人列表
know_num+=1 # 已确认身份人数
if times[P] == 2: # 灯灭且第二次放风,这个人为计数人,设置灯亮
counter = P
light = True
elif len(know_list) == 100 and know_num == 100: # 刚好100人确认身份
print(" 所有囚徒已确认身份")
break
else:
if P == counter: # 确认计数人
if not light: # 灯灭,至少有1个新人放风,灯设置为亮
light = True
know_num += 1 # 从计数人的角度来看,这里加1
if len(know_list) == 100 and know_num == 100: # 其实这里最好是判断 know_num ,因为最终是要计数人来宣布
print(" 所有囚徒已确认身份")
break
else: # 灯亮,没有人放风
pass
elif P in know_list: # 已经确认身份 什么都不用做
pass
else:
if light: # 未确认身份灯亮,放风有效,灯关闭,确认身份
light = False
know_list.append(P)
days += 1
- 感谢你赐予我前进的力量

