约瑟夫环——公式法(递推公式)

原文链接: https://blog.csdn.net/u011500062/article/details/72855826

约瑟夫问题

约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。
例如只有三个人,把他们叫做A、B、C,他们围成一圈,从A开始报数,假设报2的人被杀掉。

  • 首先A开始报数,他报1。侥幸逃过一劫。
  • 然后轮到B报数,他报2。非常惨,他被杀了
  • C接着从1开始报数
  • 接着轮到A报数,他报2。也被杀死了。
  • 最终胜利者是C

解决方案

普通解法

刚学数据结构的时候,我们可能用链表的方法去模拟这个过程,N个人看作是N个链表节点,节点1指向节点2,节点2指向节点3,……,节点N-1指向节点N,节点N指向节点1,这样就形成了一个环。然后从节点1开始1、2、3……往下报数,每报到M,就把那个节点从环上删除。下一个节点接着从1开始报数。最终链表仅剩一个节点。它就是最终的胜利者。
这里写图片描述

缺点:

要模拟整个游戏过程,时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

公式法

约瑟夫环是一个经典的数学问题,我们不难发现这样的依次报数,似乎有规律可循。为了方便导出递推式,我们重新定义一下题目。
问题: N个人编号为1,2,……,N,依次报数,每报到M时,杀掉那个人,求最后胜利者的编号。

这边我们先把结论抛出了。之后带领大家一步一步的理解这个公式是什么来的。
递推公式:
f ( N , M ) = ( f ( N 1 , M ) + M ) f(N,M)=(f(N−1,M)+M)%Nf(N,M)=(f(N−1,M)+M)%N f(N,M)=(f(N-1,M)+M)\%N f(N,M)=(f(N1,M)+M)%n

**注:**理解这个递推式的核心在于关注胜利者的下标位置是怎么变的。每杀掉一个人,其实就是把这个数组向前移动了M位。然后逆过来,就可以得到这个递推式。

因为求出的结果是数组中的下标,最终的编号还要加1

下面给出代码实现:

int cir(int n,int m)
{
	int p=0;
	for(int i=2;i<=n;i++)
	{
		p=(p+m)%i;
	}
	return p+1;
}

猜你喜欢

转载自blog.csdn.net/weixin_44489823/article/details/101178702