zoukankan      html  css  js  c++  java
  • 约瑟夫环问题,一道经典的数据结构题目

    问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

    一般我们采用一个循环队列来模拟约瑟夫环的求解过程,但是如果n比较大的时候,采用模拟的方式求解,需要大量的时间来模拟退出的过程,而且由于需要占用大量的内存空间来模拟队列中的n个人,并不是一个很好的解法。

    在大部分情况下,我们仅仅需要知道最后那个人的编号,而不是要来模拟一个这样的过程,在这种情况下,可以考虑是否存在着一种数学公式能够直接求出最后那个人的编号。

    我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):

    我们先看第一个人出列后的情况,显而易见,第一个出列的人的编号一定是m%n-1,这个人出列后,剩下的n-1个人组成了一个新的约瑟夫环,这个约瑟夫环的第一个人在最开始的环中的编号是k=m%n(就是第一个出列的人的下一个)

    k  k+1  k+2  … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

    事实上,可以把这个环又映射成为一个新的环:

    k  — 0

    k+1 — 1

    k+2 — 2

    …  ….

    k-2 — n-1

    可以看出,这就是原问题中把n替换成n-1的情况,假设我们已经求出来在这种情况下(即n-1个数字时)最后胜利的那个人的编号是n-1中的x,那个倒推回去的n个数字时那个人的编号就是我们要求的答案,显而易见,这个编号应该是(x+k)%n,而k=m%n,所以这个编号为(x+m)%n.

    那么如何知道n-1个人下面的这个x呢,yes,就是n-2个人情况下得到的x’倒推回去,那么如何知道n-2情况下的x’呢,当然是求n-3个人,这就是一个递归的过程

    f(1) = 0(f(1)就是现在还剩下1个人,那么无论m为几,这个人总会出列,因此f(1)=0)

    f(n) = (f(n-1)+m)%n

    那么我们要求f(n),就从f(1)倒推回去即可。

    #include <stdio.h>

    int main()

    {

    int n, m, i, s = 0;

    printf (“N M = “);

    scanf(“%d%d”, &n, &m);

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

    {

    s = (s + m) % i;

    }

    printf (“ The winner is %d ”, s+1);

    }

    转载请注明:约瑟夫环问题,一道经典的数据结构题目

  • 相关阅读:
    451. Sort Characters By Frequency
    424. Longest Repeating Character Replacement
    68. Text Justification
    44. Wildcard Matching
    160. Intersection of Two Linked Lists
    24. Swap Nodes in Pairs
    93. 递归实现组合型枚举
    98. 分形之城
    97. 约数之和
    96. 奇怪的汉诺塔
  • 原文地址:https://www.cnblogs.com/whiterock/p/8158812.html
Copyright © 2011-2022 走看看