有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去。
有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。
由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。
思路:
由于需要标记哪些位置是非基督徒(被扔海里的人),所以30个人的位置不能变,被扔海里的人的位置还需要留着。
所以用people来轮询报数的人的位置,由于是个圈,所以超过30之后要取余数。
用'1'表示活着,用'0'表示丢海里死掉了,是个空位置。
所以刚开始的时候有30个'1'。
然后每次数到9,就把'1'变成'0',报数的时候先判断那个位置上有没有人(是‘1’还是‘0’),没人(‘0’)就跳过。
people_list = list('1'*30) # 用1表示活着
people = 0 # 位置索引
dead = 0 # 统计死掉的人
count = 1 # 报数
while dead < 15:
people = people % 30 # 围成一个圈,所以30个人不停循环
if people_list[people] == '1': # 如果这个人还没死
if count == 9: # 如果他报了9
people_list[people] = '0' # 那他就死了
dead = dead + 1 # 死掉的人+1
count = 1 # 重新报数
else: # 如果不是报的9
count = count + 1 # 下一个人报数+1
people = people + 1 # 下一个人继续
print(people_list)
结果:
类似的思路也可以用来解决灯开关的问题:
有100盏灯,初始状态为关闭,全由拉绳开关控制。
第一次:被3整除的灯拉一次开关。
第二次:被5整除的灯拉一次开关。
第三次:被7整除的灯拉一次开关 。
最后有几盏灯亮,分别是哪几盏灯?
deng = list('开'*100)
def on_down(i):
global deng
if deng[i] == '开':
deng[i] = '关'
else:
deng[i] = '开'
for i in range(100):
if (i+1)%3 ==0:
on_down(i)
if (i+1)%5 ==0:
on_down(i)
if (i+1)%7 ==0:
on_down(i)
print(deng)
双向队列可以同时在左右两边进行增加和删除,每次把队列左边的8个人拿出来放到队列右边,这时候最左边的人就是报数9的人,毫不犹豫地杀掉,然后继续从左边取8个人放到队列右边,重复15次就完事了。
import collections
q = collections.deque([i for i in range(1, 31)]) # 从1到30的队列
for _ in range(15): # 重复15次,杀掉15个人
for _ in range(8): # 每次拿出8个人
q.append(q.popleft()) # 把这8个人从左边放到右边
q.popleft() # 拿完8个人就杀掉最左边的人(第9个人)
print(q)
结果:
其实用列表也可以更简单地实现:
lists = [i for i in range(1, 31)]
for i in range(15):
lists = lists[9:] + lists[:8] # 第9个人之后的队伍和前8个人重新组成数组
print(lists)
结果: