约瑟夫环是个经典的问题,相信会单向链表的人都会解决,但显然复杂度是O(mn),若要高效,则必须在数学分析上做点功夫。
假设这n个人编号为0~n-1,报数从0报到m-1。令k=m mod n,显然k-1为第一位退出者的编号。剩下的n-1人依次为:k,k+1,k+2,。。n-1,0,1,2,。。。,k-2。
我们换个角度看,这n-1人组成了一个新的约瑟夫环,开始新一轮的报数,而且新约瑟环中的0号就是原约瑟夫环的k号。那么按照这个数按序我们重新编号:
旧约瑟夫环(人)
k
k+1
k+2
.....
n-1
0
1
2
...
k-2
新约瑟夫环(n-1人)
0
1
。。。
n-k-1
n-k+1
n-k+2
n-k+3
.....
n-1
他们的对应关系已经很明显了,p=(p+k)mod n
既然通过n能够推出n-1,那么n-1一样能推出n-2...2,
推出1.算法已经出来了。f(i)表示i个人报数m最后剩下的人的编号。
则最后结果为f(n),有如下的递推关系
f(1)=0;
f(i)=(f(i-1)+m)mod i;i>1;
源程序为:
#include<iostream>
using namespace std;
void main()
{
long n,m,i,f=0;
cin>>n>>m;
for(i=2;i<=n;i++)
for(f+m)%i;
cout<<"最后人的编号为"<<f+1<<endl;
}
本程序采用迭代,实质是递推思想。
时间复杂度是O(n)相对于模拟算法已有很大的提高。