您的位置:首页 > 其它

【原】 POJ 3750 小孩报数问题 Joseph相关问题详解 解题报告

2010-11-09 20:06 567 查看
 

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

[code][code] #include <stdio.h>


#include <iostream>


#include <string>


#include <fstream>


 


using namespace std ;


 


//利用数组求出队顺序


void run3750()


{


ifstream in("in.txt");


 


int n,start,step ; //三个参数分别是人数,开始人的号,数到step出队一人


int i,j ;


int left ;


string name ;


 


in>>n ;


//1...n


//存放于位置对应的人名


string *strArr = new string[n+1] ;


//记录该位置上是否还有人


//1:有人


//0:已出队


int *numArr = new int[n+1] ;


for( i=0 ; i<=n ; ++i )


numArr[i]=1 ;


 


i = 1 ;


while( i<=n && in>>name )


strArr[i++] = name ;


scanf("%d,%d",&start,&step);  //scanf可以格式化输入


 


i = start ;


j = 0 ;


left = n ;


while( left > 0 )


{


if(numArr[i]==1)


++j ;


if(j==step)


{


numArr[i] = 0 ;


--left ;


   j = 0 ;


cout<<strArr[i]<<endl ;


}


/* 这样会造成当numArr[1...n]都为0(都出队之后)时的死循环


do


{


if(++i==n+1)


  i = 1 ;


}


while(numArr[i]==0) ;


*/


if(++i==n+1)


   i = 1 ;


}


 


delete []numArr ;


delete []strArr ;


}


 


 


//利用循环链表求出队顺序


//技巧:将初始指针指向start之前的节点,这样使得代码简便,并且容易处理step为1的情况


struct node


{


string name ;


node *next ;


};


 


void JosephusLinkList()


{


ifstream in("in.txt");


 


int n,start,step ; //三个参数分别是人数,开始人的号,数到step出队一人


int i ;


node *first,*last ;


node *p,*q ;


 


in >> n ;


//先构造一个节点的循环链表,设置好first和last指针


//这是必须的,不能放在循环中做,要单独做


first = new node ;


in >> (first->name) ;


first->next = first ;


last = first ;


 


//再构建2~n节点


for(i=2 ; i<=n ; ++i)


{


last = ( last->next = new node ) ;


in >> last->name ;


last->next = first ;


}


 


scanf("%d,%d",&start,&step);  //scanf可以格式化输入


 


//找到起始点,为start之前的节点,所以p初始化为last而不是first


//p = first ;


p = last ;


for( i=1 ; i<start ; ++i )


p = p->next ;


 


//模拟出队过程


while( n>0 )


{


//找到待出队节点之前的节点


for( i=1 ; i<step ; ++i )


    p = p->next ;


//记录下将出队的节点,以备输出和删除


q = p->next ;


//出队


p->next = p->next->next ;


//输出


cout<<q->name<<endl ;


//删除


delete q ;


--n ;


}


}


 


 


//求最后出队的人,复杂度O(n)


//起始编号为1时最后出队的编号


int Josep1( int n, int step )


{


/*


//递归


if(n==1)


return 1 ;


return ( Josep(n-1,step)+step-1 ) % n + 1 ;


*/




//非递归


int lastOut = 1 ;


for( int i=2 ; i<=n ; ++i )


    lastOut = (lastOut+step-1)%i+1 ; //+step-1是为了避免余数为0的情况


return lastOut ;


}


 


//求最后出队的人,复杂度O(n)


//起始编号为start时最后出队的编号,复杂度O(n)


int Josep2( int n, int start, int step )


{


/*


//递归


if(n==1)


return 1 ;


return ( test(n-1,1,step)+(start+step-1)-1 ) % n + 1 ;


*/


 


//非递归


int lastOut = 1 ; //i==1


for( int i=2 ; i<=n-1 ; ++i )    //2<=i<=n-1


    lastOut = (lastOut+step-1)%i+1 ;


return ( lastOut + (start+step-1) - 1 )%n+1 ;  //i==n


}

[/code]
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: