约瑟夫环问题

来源:互联网 发布:php判断ajax请求 编辑:程序博客网 时间:2024/06/05 15:11

一般约瑟夫环问题:N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数。问最后剩下的人的编号。例如:N = 3,K = 2。2号先出列,然后是1号,最后剩下的是3号。


实际上对于约瑟夫环问题,最常见的有2种解法。


第一种就是直接暴力模拟链表,当然这种做法的时间复杂度很高,而且实现起来还很麻烦。第二种方法就是利用递推关系,有f[1] = 0,f[i] = (f[i-1] + K) % i;一个递推就完成,时间复杂度为O(n)。


#include <iostream>#include <string.h>#include <stdio.h>using namespace std;int main(){    int n,k;    while(cin>>n>>k)    {        int ans = 0;        for(int i=2;i<=n;i++)            ans = (ans + k) % i;        cout<<ans+1<<endl;    }    return 0;}


题目:http://acm.hdu.edu.cn/showproblem.php?pid=2211


题意:对于约瑟夫环问题,我们一般求的是最后一个幸存者,而本题题意是求最后一个被杀者的编号。

分析:设f(N,K)表示N个人每第K个出列的最后取出的编号。那么f(N,K)进行第一次选取后,剩下N-N/K个人,这剩下的人里最后取出的编号为f(N-N/K,K),简记为x,那么它在前一次队列里的编号为x+(x-1)/(K-1),所以f(N,K)=(x-1)/(K-1)+x,其中x=f(N-N/K,K)。


#include <iostream>#include <string.h>#include <stdio.h>using namespace std;int Work(int n,int k){    if(n == k) return k;    int t = Work(n-n/k,k);    return (t-1)/(k-1) + t;}int main(){    int T,N,K;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&N,&K);        printf("%d\n",Work(N,K));    }    return 0;}



好了,现在我们来看一下约瑟夫环问题的升级版:

N个人坐成一个圆环(编号为1 - N),从第K个人开始报数,数到M的人出列,后面的人重新从1开始报数。问最后剩下的人的编号。例如:N = 3,M = 2,K = 1。2号先出列,然后是1号,最后剩下的是3号。


对于这个问题,我们同样有递推求解方法:

LL Josephus(LL n,LL m,LL k) //参数分别为:人数,步长,起始报数位置{    for(LL i=1; i<=n; i++)        k = (k + m - 1) % i + 1;    return k;}

这个算法的时间复杂度为O(n)。


事实上,如果我们观察上述算法中的变量k,他的初始值为第一个出圈人的编号,但在循环的过程中,我们会发现它常常处在一种等差递增的状态,我来看这个式子:k = (k + m - 1) % i +  1,可以看出,当i比较大而k+m-1比较小的时候,k就处于一种等差递增的状态,这个等差递增的过程并不是必须的,可以跳过。

我们设一中间变量 x,列出等式:k + m * x – 1 = i + x,解出x,令k = k + m * x,将i + x直接赋值给i,这样就跳过了中间共x重的循环,从而节省了等差递增的时间开销。可是其中求出来的x + i可能会超过n,这样的结果事实上已经告诉我们此时可以直接结束算法了,即:


k = k + m * (n - i) ;
i = n;

结 束。
另外对于m = 1的情况可以单独讨论:
当k == 1时,最终结果就是n;
当k != 1时,最终结果就是(k + n -  1) % n。


题目:http://acm.hdu.edu.cn/showproblem.php?pid=3089

#include <iostream>#include <string.h>#include <stdio.h>using namespace std;typedef long long LL;LL Work(LL n,LL m,LL k){    if(m == 1)        k = (k == 1 ? n:(n+k-1)%n);    else    {        for(LL i=1; i<=n; i++)        {            if(k + m < i)            {                LL x = (i-k+1)/(m-1) - 1;                if(i + x < n)                {                    i += x;                    k += m*x;                }                else                {                    k += m*(n-i);                    i = n;                }            }            k = (k+m-1)%i+1;        }    }    return k;}int main(){    LL n,k;    while(cin>>n>>k)        cout<<Work(n,k,1)<<endl;;    return 0;}



0 0