有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)
结果: