|
n个人从1开始报数,报到m的退出,剩下的人继续从1开始报数,计算最后留下的是第几个人
此题是道典型的约瑟夫环问题,解决该问题的思路有2种:
一种是常规方式,即创建环形链表,这种方式比较容易了解,即对环形链表进行循环,每次将报到m的节点删除,最后终止的条件为环形链表中的当前节点等于下一个节点指向其本身,那么这个节点就是最后留下人。
解析代码如下:
- # 链表节点
- class LinkedNode:
- def __init__(self, value):
- self.value = value
- self.next = None
- # 环形链表
- def create_linkList(n):
- # 创建头节点
- head = LinkedNode(1)
- pre = head
- for i in range(2, n + 1):
- # 创建节点
- newNode = LinkedNode(i)
- pre.next = newNode
- pre = newNode
- # 形成环形
- pre.next = head
- return head
- # 总人数
- n = 6
- # 报到的数
- m = 4
- # 如果报的数为1,则最后留下的人就是n
- if m == 1:
- print(n)
- else:
- # 获得环形链表
- head = create_linkList(n)
- pre = None
- cur = head
- # 终止条件是环形链表中的当前节点等于下一个节点指向其本身
- while cur.next != cur:
- # 模拟报数,获得报到数的节点
- for i in range(m - 1):
- pre = cur
- cur = cur.next
- # 删除报到数的节点
- pre.next = cur.next
- cur.next = None
- cur = pre.next
- print(f'最终留下的是第{cur.value}个人')
复制代码 第二种方式非常巧妙,可以通过使用递归的方式来解决该问题,即每次删除某一个报到m的人后,就对剩下的n-1个人重新编号,直到留下最后一个人,所以现在的重点就是找出删除前和删除后仍然留下人的编号的映射关系,经过分析,其映射关系如图1-1所示。
图1-1 映射关系 通过上图,很容易得出删除前和删除后仍然留下人的编号的的映射关系,假设删除前的编号为beforedel,删除后的编号为afterdel,则beforedel=(afterdel+m)%n,但是由于编号是从1开始,所以需要将beforedel加1,此外还需要保持等式两边相等,所以需要将afterdel+m减1,则最终删除前编号和删除后仍然留下的人的编号的的映射关系为beforedel=(afterdel+m-1)%n+1。
解析代码如下:
- def JosephRing(n, m):
- return n if n == 1 else (JosephRing(n - 1, m) + m - 1) % n + 1
- print(f'最终留下的是第{JosephRing(6, 4)}个人')
复制代码 |
|