http://poj.org/problem?id=3750

 

方法:

1012类似,但稍复杂

约瑟夫环问题:三个参数————人数n,开始人的号start,数到step出队一人

此问题一般有两种形式(这里都给了实现)

1、求出队顺序:利用数组或循环链表模拟出队过程,复杂度为O(step*n)
     技巧:循环链表实现时将初始指针指向start之前的节点,这样使得代码简便,并且容易处理step为1的情况
     线段树搞到O(n * logn)或树状数组优化到O(n * logn * logn) ????????????????

2、求最后出队的人:解此问题不需要模拟出队过程,复杂度降为O(n)

     思路:

     <1> start = 1
     初始n环为:1 2 3 4 ... n-1 n
     当n个人围成一圈并以m为步长第一次报数时,第m个人出列,此时就又组成了一个新的人数为n-1的约瑟夫环,从m+1开始。
     此时n-1环为:m+1 ... n-1 n 1 2 3 ... m-2 m-1 
     现在将环中编号做一个转化:
     m+1 --> 1 , m+2 --> 2 , ...... m-2 --> n-2 , m-1 --> n-1
     这又形成了一个从1开始的n-1的环,如果我们求得此环最后一个出队的编号为P(n-1,m),它也是初始n环最后一个出队元素,
     但是与它在初始n环中的编号不同,所以我们需要将它在n-1环中的编号变换回来,设原始编号为P(n,m),
     则P(n,m)=( P(n-1,m) + m-1 )%n+1,此处不写成( P(n-1,m) + m )%n是为了避免余数为0的情况。
     如何知道(n-1)个人报数的问题的解?只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况......

     递归式:P(i,step) = 1 , i==1
             P(i,step) = ( P(i-1,step)+step-1 )%i+1 , i>1
     P(n,step)求得起始编号为1时的最后出队编号。

     技巧:
     递归式中+step-1是为了避免余数为0的情况
     
     ****************************************

     <2> start任意(假设start<=n,否则start%=n)
     初始n环为:1 2 3 4 ... n-1 n
     第一个出列的编号为(start+m-1)
     此时n-1环为:start+m  start+m+1 ... n-1 n 1 2 3 ... start+m-2

     递归式:P(i,1,step) = 1 , i==1
             P(i,1,step) = ( P(i-1,1,step)+step-1 )%i+1 , 1<i<n
             P(n,start,step) = ( P(n-1,1,step)+(start+step-1)-1 )%n+1 , i==n (因为变换后的起始编号为1)
     P(n,start,step)求得起始编号为start时的最后出队编号。

 

 

Description

有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。

Input

第一行输入小孩的人数N(N<=64)
接下来每行输入一个小孩的名字(人名不超过15个字符) 
最后一行输入W,S (W < N),用逗号","间隔

Output

按人名输出小孩按顺序出列的顺序,每行输出一个人名

Sample Input

5

Xiaoming

Xiaohua

Xiaowang

Zhangsan

Lisi

2,3

Sample Output

Zhangsan

Xiaohua

Xiaoming

Xiaowang

Lisi



#include <stdio.h>
#include <iostream>
#include <string>
#include <fstream>
   5:  
namespace std ;
   7:  
//利用数组求出队顺序
void run3750()
  10: {
);
  12:  
//三个参数分别是人数,开始人的号,数到step出队一人
int i,j ;
int left ;
  16:     string name ;
  17:  
  18:     in>>n ;
//1...n
//存放于位置对应的人名
new string[n+1] ;
//记录该位置上是否还有人
//1:有人
//0:已出队
int[n+1] ;
for( i=0 ; i<=n ; ++i )
  27:         numArr[i]=1 ;
  28:  
  29:     i = 1 ;
while( i<=n && in>>name )
  31:         strArr[i++] = name ;
//scanf可以格式化输入
  33:  
  34:     i = start ;
  35:     j = 0 ;
  36:     left = n ;
while( left > 0 )
  38:     {
if(numArr[i]==1)
  40:             ++j ;
if(j==step)
  42:         {
  43:             numArr[i] = 0 ;
  44:             --left ;
  45:             j = 0 ;
  46:             cout<<strArr[i]<<endl ;
  47:         }
/* 这样会造成当numArr[1...n]都为0(都出队之后)时的死循环
        do
        {
            if(++i==n+1)
                i = 1 ;
        }
        while(numArr[i]==0) ;
        */
if(++i==n+1)
  57:             i = 1 ;
  58:     }
  59:  
delete []numArr ;
delete []strArr ;
  62: }
  63:  
  64:  
//利用循环链表求出队顺序
//技巧:将初始指针指向start之前的节点,这样使得代码简便,并且容易处理step为1的情况
struct node
  68: {
  69:     string name ;
  70:     node *next ;
  71: };
  72:  
void JosephusLinkList()
  74: {
);
  76:  
//三个参数分别是人数,开始人的号,数到step出队一人
int i ;
  79:     node *first,*last ;
  80:     node *p,*q ;
  81:  
  82:     in >> n ;
//先构造一个节点的循环链表,设置好first和last指针
//这是必须的,不能放在循环中做,要单独做
new node ;
  86:     in >> (first->name) ;
  87:     first->next = first ;
  88:     last = first ;
  89:  
//再构建2~n节点
for(i=2 ; i<=n ; ++i)
  92:     {
new node ) ;
  94:         in >> last->name ;
  95:         last->next = first ;
  96:     }
  97:  
//scanf可以格式化输入
  99:  
//找到起始点,为start之前的节点,所以p初始化为last而不是first
//p = first ;
 102:     p = last ;
for( i=1 ; i<start ; ++i )
 104:         p = p->next ;
 105:  
//模拟出队过程
while( n>0 )
 108:     {
//找到待出队节点之前的节点
for( i=1 ; i<step ; ++i )
 111:             p = p->next ;
//记录下将出队的节点,以备输出和删除
 113:         q = p->next ;
//出队
 115:         p->next = p->next->next ; 
//输出
 117:         cout<<q->name<<endl ;
//删除
delete q ;
 120:         --n ;
 121:     }
 122: }
 123:  
 124:  
//求最后出队的人,复杂度O(n)
//起始编号为1时最后出队的编号
int step )
 128: {
/*
    //递归
    if(n==1)
        return 1 ;
    return ( Josep(n-1,step)+step-1 ) % n + 1 ;
    */
 135:     
//非递归
int lastOut = 1 ;
int i=2 ; i<=n ; ++i )
//+step-1是为了避免余数为0的情况
return lastOut ;
 141: }
 142:  
//求最后出队的人,复杂度O(n)
//起始编号为start时最后出队的编号,复杂度O(n)
int step )
 146: {
/*
    //递归
    if(n==1)
        return 1 ;
    return ( test(n-1,1,step)+(start+step-1)-1 ) % n + 1 ;
    */
 153:  
//非递归
//i==1
//2<=i<=n-1
 157:         lastOut = (lastOut+step-1)%i+1 ;
//i==n
 159: }

相关文章: