约瑟夫循环问题,不过此题的n很大,不能使用O(n)的递推方法,会TLE的
参考了网上的优化方法,是在O(n)的递推方法上优化的
观察递推式 num = (num+m)%i
当num,m比较小,而i比较大时,按照递推式,num的变化实际上是每次增加m的
而增加m的次数其实是可以计算出来的
所以当num+m >= i时我们使用普通递推式, 而num+m < i时我们可以使用下面的方法
设增加次数为x,x要满足下式
num+x*m < i+(x-1) //注意后面是x-1,因为每次都是当num增加完后才增加i的,可以用小数据模拟一下就清楚了
解次式可得
x < (i-num)/(m-1)
即x = floor((i-num)/(m-1))-1
这样我们就可以直接用下面的式子来跳过这些num递增的步骤了
num += x*m
i += x
但要注意此处的 i+x可能会大于n
这时候,其实我们已经可以求得最终解了,只要加个特判就可以,详细可以见注释
//约瑟夫循环问题
//编号从0开始
//假设n = 4, m = 2, k = 1
//第一个出队的人的编号是3
LL josephus(LL n, LL m, LL k)//分别为:人数,出圈步长,起使报数位置
{
LL num = 0;
if(m == 1)
{
return (n-1+k)%n;
}
for(LL i = 2; i <= n;)
if((num+m) < i)
{
LL temp = (i-1-num)%(m-1)? (i-1-num)/(m-1): (i-1-num)/(m-1)-1;
if(i+temp > n) //当i+temp > n时特判
{
num += (n+1-i)*m; //直接得到最终解
break;
}
num += temp*m;
i += temp;
}
else
{
num = (num+m)%i; //普通递推
++i;
}
num = (num+k)%n;
return num;
}