约瑟夫循环问题,根据http://blog.csdn.net/ZCSYLJ/article/details/6830703的文章写出自己的一些心得体会。
首先在一个序列中,假如该序列有n个元素,并且以m报数。假设给这个序列进行编号,第一个为0,第二个为1.......则这个序列就是0,1,2,3,........n-1
那么第一个出来的人必然是m%n-1。之后便是一个思维的关键点,我们把m%n-1之前的数移到最后面并且去除m%n-1这个元素从而组成一个新的序列。
为什么要把之前的元素转移到后面呢?
因为这是约瑟夫循环算法的一个必须条件,没有此条件不能完成下面我要说的一个循环。不妨假设在这一步中,只剔除m&n-1这个元素。之后继续进行报数,虽然这样也是可行的,但是会造成许多的麻烦,原本的2去除,之后需要去除5,之后是8.......并且这样一来你去掉的那个数字所在的位置要怎么办的,是要以0代替?还是什么别的?由此一来便十分困难,所以当元素转移之后,每次都是编号为2的元素剔除(不包括最后几次)。
之后我们需要对这个序列进行重新编号,原来的编号是:m%n,m%n+1,m%n+2,.......n-1,0,1,2,......m%n-2。
现在的编号是 :0,1,2,3,4...........n-2。
在这里考虑m%n是考虑到m>n的情况
之后重复按照先前的两个步骤循环就可以得到最后一个只有一个数字的序列,而这最后一个序列就是你想得到的结果。
所以这就是一个数学的关于J[n]与J[n-1]之间的关系。并且已经知道了J[0]必然是等于0的。所以现在必须知道J[n]与J[n-1]之间的关系到底是什么。(J[N]代表最终只剩下一个人该人的编号)
经过递归我们知道了,由于在转移之前,剔除的数的后一个数现在的编号是0,之前的编号是m%n,所以J[n-1]+m%n=J[n]。考虑到原编号为0之后的情况,需要(J[n-1]+m%n)%n。所以得到了J[n]=(J[n-1]+m%n)%n。这个函数说白了就是一个编号转换的函数。其实大家如果看不懂这个函数时,往往都以为是这个函数是解出这题的关键点,其实不然。愚以为把元素放到后面的这个做法才是最重要的:把一部分元素放到后面,之后重新编号,递归。这才是这个约瑟夫循环报数的精华思想。
所以下面就可以附上代码了:
#include<iostream>
using namespace std;
int main()
{
int m,n;cin>>m>>n;
int sum=0;
for(int i=2;i<=n;i++)sum=(sum+m%i)%i;
cout<<sum+1;//考虑到实际问题第一个人的编号一般不是0而是1
}