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);
}

 

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

转载于:https://www.cnblogs.com/Sunlnx/p/3322295.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值