第18 题: 题目:n 个数字(0,1,…,n-1)形成一个圆圈,从数字0 开始每次从这个圆圈中删除第m 个数字(第一个为当前数字本身,第二个为当前数字的下一个数 字)。

当一个数字删除后,从被删除数字的下一个继续删除第m 个数字。

求出在这个圆圈中剩下的最后一个数字。

思路:

      把这个题目中的开始数字改下以便下面的说明,n 个数字(1,…,n-1,n)形成一个圆圈,从数字1 开始每次从这个圆圈中删除  第(第说明与开始数字无关)m 个数字(第一个为当前数字本身,第二个为当前数字的下一个数 字)。

     这个是Josephus问题的推广(Josephus问题是m = 3)。

     这个题目出题人的意图应该是采用模拟的方法吧,这种方法网上很多。正好前段时间上了具体数学这门课,下面利用《具体数学》中提到的知识来解决这样的问题。可以参考具体数学第三章第三节。

    下面是对原文的一段摘抄:(Josephus问题是m = 3)

     每当一个人越过,我们就赋予一个新的数。于是,1和2变成了n+1和n+2,则3被执行;4和5变成n+3和n+4,则6被执行;……,3k+1和3k+2变成n+2k+1和n+2k+2,则3k+3被执行;……,则3n被执行(或留下幸存),例如当n=10,数是

          1          2          3          4          5          6          7          8          9          10

         11       12                    13        14                    15        16                     17

          18                               19        20                                 21                    22

                                              23        24                                                          25

                                              26                                                                       27

                                              28

                                             29

                                              30

 数3k最后以消除第k个人而告终。所以如果我们能计算出人数3n的原先的数,则我们能断定谁是幸存者。

    若N>n,人数N一定要有一个以前的数,且我们能找到它。如下:我们有N=n+2k+1或N=n+2k+2,因此k= (int)((N-n-1)/2),以前的数分别是3k+1或3k+2,也就是说,它是3k+(N-n-2k)=k+N-n,因此我们能计算幸存者数J3(n)如下:

    N = 3n;

    while  N>n    do     N:=(int)  ((N-n-1)/2 ) + N -n;

   J3(n)  :=  N.

   利用D = 3n+1-N来替换N,我们可以得到改进的算法:

  D:=1;

  while  D <=  2n    do   D :=  ceil(   3*D/2 );//ceil是c中的取上整函数

   J3(n) :=  3n + 1 -D.

 同样的道理消除第m个人。

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
	double a = 1.0;
	int m = 0,n = 0;

	cin >> m >> n;

	while ( a <= (m-1)*n )
	{
		a = ceil ( m*a/(m-1) );
	}

	cout << (m*n + 1 - (int)a  - 1) << endl;//原文是从0开始,在此基础上-1.
	return 0;
}

     

点赞