zoukankan      html  css  js  c++  java
  • 约瑟夫环问题

    先看下资料,数学公式来的

    资料转自:http://jackiesteed.blog.163.com/blog/static/165506750201051552930758/

    约瑟夫环问题是一道经典的数据结构题目
    问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
    一般我们采用一个循环队列来模拟约瑟夫环的求解过程,但是如果n比较大的时候,采用模拟的方式求解,需要大量的时间来模拟退出的过程,而且由于需要占用大量的内存空间来模拟队列中的n个人,并不是一个很好的解法。
    在大部分情况下,我们仅仅需要知道最后那个人的编号,而不是要来模拟一个这样的过程,在这种情况下,可以考虑是否存在着一种数学公式能够直接求出最后那个人的编号。

    我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
    我们先看第一个人出列后的情况,显而易见,第一个出列的人的编号一定是m%n-1,这个人出列后,剩下的n-1个人组成了一个新的约瑟夫环,这个约瑟夫环的第一个人在最开始的环中的编号是k=m%n(就是第一个出列的人的下一个)
    k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。
    事实上,可以把这个环又映射成为一个新的环:
    k  --- 0
    k+1 --- 1
    k+2 --- 2
    ...  ....
    k-2 -- n-1
    可以看出,这就是原问题中把n替换成n-1的情况,假设我们已经求出来在这种情况下最后胜利的那个人的编号是x,那个倒推回去的那个人的编号就正好是我们要求的答案,显而易见,这个编号应该是(x+k)%n
    那么如何知道n-1个人下面的这个x呢,yes,就是n-2个人情况下得到的x'倒推回去,那么如何知道n-2情况下的x'呢,当然是求n-3个人,这就是一个递归的过程
    f(1) = 0(f(1)就是现在还剩下1个人,那么无论m为几,这个人总会出列,因此f(1)=0)
    f(n) = (f(n-1)+m)%n
    那么我们要求f(n),就从f(1)倒推回去即可
    int f(int n, int m)
    {
       int r = 0;
       for(int i = 2; i <= n; i++)
           r = (r + m) % i;
       return r + 1; //这是因为日常生活中编号总是从1开始
    }

    pku3715 And Then There Was One

    基本的约瑟夫环问题,可以直接用数组模拟,也可以用数学方法做,当然,时间差很大

    pku 3715
    数学方法
    #include
    <iostream>
    using namespace std;
    int main()
    {
    int n,k,m;
    while(scanf("%d %d %d",&n,&k,&m),n||k||m)
    {
    int i,d,s=0;
    for(i=2;i<=n;i++) s=(s+k)%i;
    k
    =k%n;if(k==0) k=n;
    d
    =(s+1)+(m-k);
    if(d>=1 &&d<=n) printf("%d\n",d);
    else if(d<1) printf("%d\n",d+n);
    else if(d>n) printf("%d\n",d%n);
    }
    return 0;
    }
    ------------------------------------------------------------------------------
    数组模拟
    #include
    <iostream>
    using namespace std;
    int a[10001];
    int main()
    {
    int n,k,m;
    while(scanf("%d %d %d",&n,&k,&m),n||m||k)
    {
    int i,cur;
    for(i=0;i<m-1;i++) a[i]=i;
    for(;i<n;i++)a[i]=i+1;
    cur
    =m-1;
    while(--n){
    cur
    =(cur+k-1)%n;
    for(i=cur+1;i<n;i++)
    a[i
    -1]=a[i];
    }
    printf(
    "%d\n",a[0]+1);
    }
    return 0;
    }

    hdu 1443 Joseph

    可以先打表,在求m值的时候,可以枚举每一种可能

    hdu 1443
    #include<stdio.h>
    int f[15];
    bool jos(int ,int );
    int main()
    {
    int i,j,k,m,n;
    for(i=1;i<14;i++)
    for(j=i;;j++)
    if(jos(i,j))
    {
    f[i]
    =j;
    break;
    }
    while(scanf("%d",&n),n)
    printf(
    "%d\n",f[n]);
    return 0;
    }
    bool jos(int n,int m)//n代表每一边的人数,m代表所报的数
    {
    int start=0,end=n-1,killed;
    int i,j;
    bool flag=true;
    for(i=2*n;i>n;i--)//从头进行模拟
    {
    killed
    =(m-1)%i;//i代表剩余的人数,killed表示出列的相对编号
    if((killed<=end)&&(killed>=start))
    {
    flag
    =false;
    break;
    }
    //重新修改符号条件的范围
    start=((start-m)%i+i)%i;//按照那个编号变化公式重新进行赋值
    end=((end-m)%i+i)%i;//加上i,再%i的目的是保证它非零
    }
    return flag;
    }
  • 相关阅读:
    桟错误分析方法
    gstreamer调试命令
    sqlite的事务和锁,很透彻的讲解 【转】
    严重: Exception starting filter struts2 java.lang.NullPointerException (转载)
    eclipse 快捷键
    POJ 1099 Square Ice
    HDU 1013 Digital Roots
    HDU 1087 Super Jumping! Jumping! Jumping!(动态规划)
    HDU 1159 Common Subsequence
    HDU 1069 Monkey and Banana(动态规划)
  • 原文地址:https://www.cnblogs.com/nanke/p/2168796.html
Copyright © 2011-2022 走看看