• N人报数第M人出列游戏问题(约瑟夫问题)


      这是一道华为的机试题,后来才知道也叫约瑟夫问题,题目是这样的:有n个人围成一圈,玩一个游戏,规则为将该n个人编号为1,2,......n, 从编号为1的人开始依次循环报数,报道第m的时候将第m个人从队伍中出列,然后从下一个人开始,又依次从1,2,....m报数,每次将报数为第m的人出列,直到最后只剩下1个人为止,则剩下的最后一個人获得游戏的胜利,现在给定n和m(n,m>0),让你求出哪位队员将获得游戏的胜利?

      分析如下

      假设每次队列的编号为:1, 2, 3......n,那么报数为m的人,编号应该为m % n,如下

      1, 2, 3, ......m%n-1, m%n, m%n+1......n

      我们取n=5, m=3为例,方便分析,则第一次报数情况为:

      1, 2, 3, 4, 5

      现在,报数应该从编号为4的人开始,咱们将整个队列重新编号为如下(应为下一轮报数第一个人应该为编号为4的人):

      3, 4,      1, 2

      由此可见,问题规模缩减为n=4, m=3的问题,继续进行如上的运算,依次为:

          1,      2, 3

       1,       2

          1

      最后问题化解为规模为n=1, m=3的问题, 此时毫无疑问,结果即为1, 那么各个问题的结果之间有没有关系呢?当然,是有的,我们将上述各个子问题的中间结果写成如下形式,方便发现规律(带下划线的表示该规模问题对应的报数为m的人,X表示为空):

      1  2  3  4  5

      3  4  X  1  2

      X  1  X  2  3

      X  1  X  2  X

      X  X  X  1  X  

    可知最后结果为1, 但是其对应的列号为4,其实4就是问题的答案,那么如何得到4呢?事实上,每一步中间结果是有关系的,1推出2, 2推出2, 2推出1, 1推出4, 即得到了结果,至于如何推出的,接下来我们继续分析:

      假设第i次的报数情景为:

      1, 2, 3, ......m%n-1, m%n, m%n+1......n,

      当:m%n <n-1  

        第i+1次的报数情景则应该为

        n-m%n+1, n-m%n+2, n-m%n+3,...... 1, 2, ......n-m%n

        任意一个两次都出现过的人,编号(设为B)关系有Bi = (Bi+1 + m%n)  % n;

      当:m%n = n-1时:

        第i+1次报数的情景则为

        1, 2, 3, ......m%n-1

        任意一个两次都出现在队伍中的人,编号关系为Bi = Bi+1 + m%n

      综合根据第i+1次结果得到第i次的结果表达式为:

      Bi = (Bi+1 + m % n) %n == 0?   Bi+1 + m%n   :   (Bi+1 + m % n) %n

     用递归实现的C代码如下

      

    #include <stdio.h>
    int solve(int n, int m) {
        int t;
     if (n == 1)
      return 1;
     return ((t= ((solve(n - 1, m) + m % n) % n)) == 0)?n:t;
    }
    int main(int argc, char **argv) {
     int n, m, r;
     scanf("%d %d", &n, &m);
     r = solve(n, m);
     printf("%d", r);
    }

      当然,递归效率还是比较低,这个转非递归的迭代很简单。

  • 相关阅读:
    Go语言http之请求接收和处理 代码
    C++之IO流的状态以及使用
    C++之指向函数的指针
    C++之数组类型的形参
    C++之vector类型的形参
    C++之形参
    C++之运算符
    C++之多维数组
    C++之动态数组
    C++之指针
  • 原文地址:https://www.cnblogs.com/Sunlnx/p/3322295.html
Copyright © 2020-2023  润新知