当一个数字删除后,从被删除数字的下一个继续删除第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;
}