zoukankan      html  css  js  c++  java
  • 【原】 POJ 3750 小孩报数问题 Joseph相关问题详解 解题报告

    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



       1: #include <stdio.h>
       2: #include <iostream>
       3: #include <string>
       4: #include <fstream>
       5:  
       6: using namespace std ;
       7:  
       8: //利用数组求出队顺序
       9: void run3750()
      10: {
      11:     ifstream in("in.txt");
      12:  
      13:     int n,start,step ; //三个参数分别是人数,开始人的号,数到step出队一人
      14:     int i,j ;
      15:     int left ;
      16:     string name ;
      17:  
      18:     in>>n ;
      19:     //1...n
      20:     //存放于位置对应的人名
      21:     string *strArr = new string[n+1] ;
      22:     //记录该位置上是否还有人
      23:     //1:有人
      24:     //0:已出队
      25:     int *numArr = new int[n+1] ;
      26:     for( i=0 ; i<=n ; ++i )
      27:         numArr[i]=1 ;
      28:  
      29:     i = 1 ;
      30:     while( i<=n && in>>name )
      31:         strArr[i++] = name ;
      32:     scanf("%d,%d",&start,&step);  //scanf可以格式化输入
      33:  
      34:     i = start ;
      35:     j = 0 ;
      36:     left = n ;
      37:     while( left > 0 )
      38:     {
      39:         if(numArr[i]==1)
      40:             ++j ;
      41:         if(j==step)
      42:         {
      43:             numArr[i] = 0 ;
      44:             --left ;
      45:             j = 0 ;
      46:             cout<<strArr[i]<<endl ;
      47:         }
      48:         /* 这样会造成当numArr[1...n]都为0(都出队之后)时的死循环
      49:         do
      50:         {
      51:             if(++i==n+1)
      52:                 i = 1 ;
      53:         }
      54:         while(numArr[i]==0) ;
      55:         */
      56:         if(++i==n+1)
      57:             i = 1 ;
      58:     }
      59:  
      60:     delete []numArr ;
      61:     delete []strArr ;
      62: }
      63:  
      64:  
      65: //利用循环链表求出队顺序
      66: //技巧:将初始指针指向start之前的节点,这样使得代码简便,并且容易处理step为1的情况
      67: struct node
      68: {
      69:     string name ;
      70:     node *next ;
      71: };
      72:  
      73: void JosephusLinkList()
      74: {
      75:     ifstream in("in.txt");
      76:  
      77:     int n,start,step ; //三个参数分别是人数,开始人的号,数到step出队一人
      78:     int i ;
      79:     node *first,*last ;
      80:     node *p,*q ;
      81:  
      82:     in >> n ;
      83:     //先构造一个节点的循环链表,设置好first和last指针
      84:     //这是必须的,不能放在循环中做,要单独做
      85:     first = new node ;
      86:     in >> (first->name) ;
      87:     first->next = first ;
      88:     last = first ;
      89:  
      90:     //再构建2~n节点
      91:     for(i=2 ; i<=n ; ++i)
      92:     {
      93:         last = ( last->next = new node ) ;
      94:         in >> last->name ;
      95:         last->next = first ;
      96:     }
      97:  
      98:     scanf("%d,%d",&start,&step);  //scanf可以格式化输入
      99:  
     100:     //找到起始点,为start之前的节点,所以p初始化为last而不是first
     101:     //p = first ;
     102:     p = last ;
     103:     for( i=1 ; i<start ; ++i )
     104:         p = p->next ;
     105:  
     106:     //模拟出队过程
     107:     while( n>0 )
     108:     {
     109:         //找到待出队节点之前的节点
     110:         for( i=1 ; i<step ; ++i )
     111:             p = p->next ;
     112:         //记录下将出队的节点,以备输出和删除
     113:         q = p->next ;
     114:         //出队
     115:         p->next = p->next->next ; 
     116:         //输出
     117:         cout<<q->name<<endl ;
     118:         //删除
     119:         delete q ;
     120:         --n ;
     121:     }
     122: }
     123:  
     124:  
     125: //求最后出队的人,复杂度O(n)
     126: //起始编号为1时最后出队的编号
     127: int Josep1( int n, int step )
     128: {
     129:     /*
     130:     //递归
     131:     if(n==1)
     132:         return 1 ;
     133:     return ( Josep(n-1,step)+step-1 ) % n + 1 ;
     134:     */
     135:     
     136:     //非递归
     137:     int lastOut = 1 ;
     138:     for( int i=2 ; i<=n ; ++i )
     139:         lastOut = (lastOut+step-1)%i+1 ; //+step-1是为了避免余数为0的情况
     140:     return lastOut ;
     141: }
     142:  
     143: //求最后出队的人,复杂度O(n)
     144: //起始编号为start时最后出队的编号,复杂度O(n)
     145: int Josep2( int n, int start, int step )
     146: {
     147:     /*
     148:     //递归
     149:     if(n==1)
     150:         return 1 ;
     151:     return ( test(n-1,1,step)+(start+step-1)-1 ) % n + 1 ;
     152:     */
     153:  
     154:     //非递归
     155:     int lastOut = 1 ;                     //i==1
     156:     for( int i=2 ; i<=n-1 ; ++i )         //2<=i<=n-1
     157:         lastOut = (lastOut+step-1)%i+1 ;
     158:     return ( lastOut + (start+step-1) - 1 )%n+1 ;  //i==n
     159: }

    如果您满意我的博客,请点击“订阅Allen Sun的技术博客”即可订阅,谢谢:)

    原创文章属于Allen Sun
    欢迎转载,但请注明文章作者Allen Sun和链接
  • 相关阅读:
    [导入]自由的生活
    [导入]宁静
    [导入]书店
    [导入]娶老婆的15条金科玉律
    [导入]静静的日子
    [导入]生活无聊的日子
    [导入]新的任务
    [导入]问题:我是一个内向的男生。请问怎么追求自己喜欢的女孩
    [导入]奋斗
    java 多种方式文件读取
  • 原文地址:https://www.cnblogs.com/allensun/p/1873019.html
Copyright © 2011-2022 走看看