关于 约瑟夫环 问题

/*  此处借助九度oj上的题目!

题目描述:

每年六一儿童节,JOBDU都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为JOBDU的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为1的小朋友开始报数。每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续1...m报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到JOBDU名贵的名侦探柯南典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?

输入:

输入有多组数据。

每组数据一行,包含2个整数n(0<=n<=1,000,000),m(1<=m<=1,000,000),n,m分别表示小朋友的人数(编号1....n-1,n)HF指定的那个数m(如上文所述)。如果n=0,则结束输入。

输出:

对应每组数据,输出最后拿到大奖的小朋友编号。


*/


本质就是 约瑟夫环


我们一般的约瑟夫环的思路是:先建立环,然后在绕圈子,代码入如下:

#include <stdio.h>


typedef struct link
{
        long id;
        struct link * next;
}link, *p_link;


int main()
{
    long n, m, count, i;
    p_link head;
    p_link tmp, t;
    
    while( scanf("%ld", &n) != EOF )
    {
           if( !n )
           {
               break;
           }
           
           scanf("%ld", &m);
           
           head = ( p_link )malloc( sizeof( link ) );
           head->id = 1;
           head->next = head;
           
           t = head; 
           for( i = 1; i < n; i++ )
           {
                tmp = ( p_link )malloc( sizeof( link ) );
                tmp->id = i + 1;
                tmp->next = t->next;
                t->next = tmp;
                t = tmp;
           }


           count = 0;
           tmp = head;
           while( n > 1 )
           {
                  count++;
                  if( count == m )
                  {
                      n--;
                     // printf("%d ", tmp->id);
                      count = 0;
                      tmp->id = tmp->next->id;
                      t = tmp->next;
                      tmp->next = t->next;
                      free( t );
                  }
                  else
                  {
                      tmp = tmp->next;
                  }
           }
           
           printf("%ld\n", tmp->id);
    }
    
    return 0;
}


     当数据量很大的时候,上述算法消耗时间很多!所以改进如下!;


       第一个人(编号一定是(m)%n) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环:
  k k+1 k+2 ... n-2, n-1, n, 1, 2, ... k-2

      (以编号为k = ( m +1)%n的人开始)

  并且从k开始报1。
  现在我们把他们的编号做一下转换:( 这是核心 )
  k --> 1
  k+1 --> 1
  k+2 --> 3
...........................................
  k-3 --> n-1
  k-2 --> n
  序列1: 1, 2, 3 … n-2, n-1, n
  序列2:1, 2, 3 … k-1, k+1, …, n-2, n-1, n
  序列3: k, k+1, k+2, k+3, …, n-2, n-1,n, 1, 2, 3,…, k-1
  序列4: 1, 2, 3 …, 5, 6, 7, 8, …, n-3, n-2,n-1

       成为了(n-1)个人报数的子问题,这里就是递归的思想!!!!

      我们可以得到:
                             f[i] = ( f[i-1] + m) % i;  ( i > 1 )   ( %i的原因是:因为每一次的i都是一个子问题,呵呵~ )

#include <stdio.h>


int main()
{
    int n,m,i,r;


    while(scanf("%d",&n) == 1)
    {
           if( !n )
           {
               break;
           }
           
           scanf("%d",&m);
           r = 0;


           for( i = 2; i <= n; i++ )
           {
                r = (r + m) %i;        // 这里!!!!
           }


           printf("%d\n",r + 1);
     }


     return 0;
}









你可能感兴趣的:(关于 约瑟夫环 问题)