约瑟夫环问题

约瑟夫环问题:有编号从1到N的N个人坐成一圈报数,报到M的人出局,下一位再从1开始, 如此持续,直止剩下一位为止,报告此人的编号X。输入N,M,求出X。

对于这个问题,有很多解法。包括一些基本的解法和一些优化的算法。

基本解法一:

这个方法比较直观,就是使用循环列表来模拟约瑟夫环。但是复杂度比较高,为O(m*n)

具体的代码如下:

ExpandedBlockStart.gif View Code
/*
 * =====================================================================================
 *
 *       Filename:  c069_2.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/26/2012 11:28:44 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Hu Fangzhen (hfz), xkfzlnx@gmail.com
 *        Company:  gucas
 *
 * =====================================================================================
 
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef  struct _node_t
{
     int num;
     struct _node_t *next;  
}node_t;

node_t *list_create( int n)
{
    node_t *p_ret=NULL;
     if( 0!=n)
    {
         int i;
        node_t *p_node=(node_t*)malloc(n* sizeof(node_t));
         if(p_node==NULL)
        {
            printf( " no enough memorry\n ");
            exit(- 1);
        }
        i= 1;
        p_ret=p_node;
         for(;i<n;i++)
        {
            p_node->num=i;
            p_node->next=p_node+ 1;
            p_node=p_node->next;
        }
        p_node->num=n;
        p_node->next=p_ret;
    }

     return p_ret;
}
int main()
{
     int n,m;
     int i;
    node_t *p_list,*p_iter;
    printf( " Enter n and m: ");
    scanf( " %d %d ",&n,&m);
    p_list=list_create(n);
     if(p_list==NULL)
    {
        printf( " can't create linklist\n ");
         return - 1;
    }
    p_iter=p_list;
     while(p_iter->next!=p_list)
    {
        printf( " %d  ",p_iter->num);
        p_iter=p_iter->next;
    }
    printf( " %d \n ",p_iter->num);

    p_iter=p_list;
     while(p_iter!=p_iter->next)
    {
         for(i= 1;i<m- 1;i++)
            p_iter=p_iter->next;
        printf( " %d kicked\n ",p_iter->next->num);
        p_iter->next=p_iter->next->next;
        p_iter=p_iter->next;
    }
    printf( " %d left\n ",p_iter->num);

    free(p_list);
}

 参考:http://blog.minidx.com/2008/01/28/448.html

基本解法二:

这个方法也是比较直观的,使用数组进行模拟。

ExpandedBlockStart.gif View Code
/*
 * =====================================================================================
 *
 *       Filename:  c069.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/26/2012 11:07:19 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Hu Fangzhen (hfz), xkfzlnx@gmail.com
 *        Company:  gucas
 *
 * =====================================================================================
 
*/
#include <stdio.h>
#define NMAX 50
int main()
{
     int n,m;
     int arr[NMAX];
     int i,count,k;
    printf( " Enter n and m: ");
    scanf( " %d %d ",&n,&m);
     // 初始化编号
     for(i= 0;i<n;i++)
        arr[i]=i+ 1;

    count= 0;
    i= 0;
    k= 0; //
// 不断的循环,直到剩余一个人
     while(count<n- 1)
    {
         if(arr[i]!= 0)
            k++;
         if(k==m) // 数到m的人要被剔除
        {
            printf( " %d kicked\n ",arr[i]);
            arr[i]= 0; // 设为0表示被剔除
            k= 0; // 重新开始数
            count++; // 计数剔除人的数目
        }
// 数组不断的循环
        i++;
         if(i==n)
            i= 0;
    }
// 找到编号不为0的即为最后剩下的
     for(i= 0;i<n;i++)
         if(arr[i]!= 0)
             break;
    printf( " %d left\n ",arr[i]);
    

}

C++代码:

 #include <iostream>
 #include <vector>
 #include <algorithm>
 #include <iterator>
  using  namespace std;
  int main(){
      int n,m;
     cin>>n>>m;
     vector< int> vr(n, 0);
      for( int i= 1,pos=- 1;i<=n;i++){
          int j= 0;
          while(j<m) {
             pos=(pos+ 1)%n;
              if(vr[pos]== 0)
                 j++;
         }
         vr[pos]=i;
     }
     copy(vr.begin(),vr.end(),ostream_iterator< int>(cout, "   "));      
     cout<<endl;
 }

 

下面的一个算法是优化算法,特别巧妙:

ExpandedBlockStart.gif View Code
int f( int n,  int m)
{
     int i, r =  0;
     for (i =  2; i <= n; i++)
        r = (r + m) % i;
     return r+ 1;
}

 这是一个递归的

ExpandedBlockStart.gif View Code
int f( int n,  int m)
{
   if (n >  1)
     return (m + f(n -  1, m)) % m;
   else
     return  0;
}

 其实这个两个看起来比较简单的算法是经过一些推导的。

这儿(http://baike.baidu.com/view/717633.htm和http://wenwen.soso.com/z/q32613561.htm)有一些讲解,但是我现在还没有看明白,等以后明白之后再补上吧。 

转载于:https://www.cnblogs.com/xkfz007/archive/2012/02/26/2368531.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值