约瑟夫环问题的三种解法


约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报到k的人将被杀掉,接着下一个人又从1开始报,直到最后剩下一个,求最后留下的人的下标。

题目集合

G - Josephus Problem

Josephus again

解法1:暴力

可以直接暴力求解,时间复杂度为O(nk)

解法2:递推

reference

设$f(n,k)$为当n个人围成一圈时,最后留下的人的下标。 对于$f(n-1,k)$来说,其结果相当于$f(n,k)$的结果向前移动$k\%(n-1)$位。

因为对于$f(n,k)$来说,去掉第一轮报的数$(k\%n)$后,现在就只剩下$n-1$个数,并且是以$(k\%(n-1)+1)$作为第一个数,即所有数向前移动$k\%(n-1)$位。现在的结果就为$f(n-1,k)$

对于f(5,3)来说,其结果为4。

 

 

 当其去掉第一轮报的数后,其向前移动了$(3\%4)$位,以4为起始,$f(4,3)$结果为1,对应着$f(5,3)$的结果4向前移动了3位

 

 

 

 所以反过来看即为,即为$f(n-1,k)$的结果向后移动$k\%(n-1)$位

即$f(n+1,k)=(f(n,k)+k\%n)\%n$   (x下标从0开始,因为取模结果为[0,n-1])

时间复杂度为$O(n)$

ll josephus2(ll n,ll k)
{
    ll pos=0;
    for(int len=1;len<=n;len++)
    {
        pos = (pos+k)%len;
    }
    return pos+1;
}
递推代码

 

解法3:

reference

如果当前这一位人没被杀掉,则他可以放在幸存者的末尾,直到幸存者数量为1

所以对于下标为i的人,如果在他前面已经被杀掉了q个人,那么他的新的下标为$n+q(k-1)+x,(1\leq x <k)$

如下图所示,最后被淘汰的编号一定是$n*k$,所以幸存者最后的编号是$n*k$

 

 我们现在需要从幸存者最后的编号中恢复出最初编号

假设幸存者这一次的编号为​$pos_{i}$,在他后面包括他还有$x$位幸存者,则$[pos_{i-1},pos_{i})$间一定有x个不能被k整除的数

这样才能使在他后面包括他还有$x$位幸存者。所以根据这一推论,我们即可恢复最初的幸存者位置。

ll josephus(ll n,ll k)
{
    ll ans = n * k;
    ll cnt = 1; // 当前位置r与上一个位置l,[l,r)中有多少个不能被k整除的数
    ll tmp;
    while (ans>n)
    {
        ll t1 = cnt/(k-1);
        ll t2 = cnt%(k-1);
        if(ans%k>t2)
        {
            tmp = ans-(k*t1)-t2;
        }
        else
        {
            ll res = ans%k+(k-1)-t2;
            tmp = (ans-ans%k)-(k*(t1+1))+res;
        }
        cnt = cnt+ceil((ans-1)/k)-ceil((tmp-1)/k);
        ans = tmp;
    }
    return ans;
}
代码

时间复杂度不好算,反正挺快的,大概是$O(logn)$的几十倍,参考链接写的是$O(logn)$,orz。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM