zoukankan      html  css  js  c++  java
  • 关于约瑟夫问题的学习

    本来我是不想学这个东西的,但是谁让他考试考到了呢?

    约瑟夫问题:

    n个人(编号为0,1,...,n-1)围成一个圈子,从0号开始依次报数,每数到第m个人,这个人就得自杀,之后从下个人开始继续报数,直到所有人都死亡为止。问最后一个死的人的编号。

    方法1:暴力 O(nm)

     会打码的都知道。

    方法2:白书P65 时间O(n) 空间O(1)

    这是一个很容易理解的算法。

    如果只关心最后一个人的编号,可以用$f(n)$表示0~n-1的n个数,从0开始每m个数删除一个,最后留下来的数字编号,那么有递推式$f(n)=(f(n-1)+m)  mod  n$。

    怎么得来的呢?我们先用几个例子来看一看。

    当n=5,m=3时:

    0    1    2    3    4

    死掉一个2:

    0    1    3    4

    相当于:

    3    4    0    1

    用这样排列方式的原因是我们可以发现

    0    1    2    3

    3    4    0    1

    是可以一一对应的。

    具体就是说$(0+3)  mod  5 = 3$、$(1+3)  mod  5 = 4$、$(2+3)  mod  5 = 0$、$(3+3)  mod  5 = 1$。

    那么当m相同时,如果我们知道n=4的答案,就可以直接用此规律算出n=5的答案。

    方法3:时间O(log n),空间O(log n)

    仍是递推,但是并不是很复杂。

    我们现在思考每轮(如果目前有n个人,0到n-1全都报一遍数叫做一轮)前后的情况。

    如果n=8,m=3,则:

    0    1    2    3    4    5    6    7

    死掉2和5:

    0    1    .     3    4     .    6    7

    重新编号:

    2    3    .     4    5     .    0    1

    注意,我专门把死掉两个人的位置空出来,是因为这样子更直观。

    我们把重新编号的序列分为两部分。一部分是2~5,一部分是0~1。

    假如在重新编号后最后死的人的编号是$x$,我们需要得到的答案是$Ans$。

    那么有:

    $egin{cases}& Ans =x + ( lfloor frac{n}{m} floor imes m ) , x leq n mod m \ & Ans = x - (n  mod m) + ( lfloor frac{x-n mod m}{m-1} floor ) , x > n  mod m end{cases}$

    但是注意,在$n<m$的时候,这样做就没有意义了,所以这时候只能用方法2的递推式了。

    方法4:时间O(log n),空间O(1)

    这个方法不仅可以求最后一个死的人的编号,而且可以求第k个(从0开始数)死的人的编号,而且写法贼简便,复杂度贼优秀,是当之无愧的好算法。

    但是就没有前几个算法那么好懂了。

    首先我们知道第$k$个自杀的人就是第$(k+1) imes m-1$次报数的人,根据他之前每次报数的时刻来确定他的编号。

    例如n=5,m=3,k=5:

    报数的时刻:0    1    2    3    4    5    6    7    8    9    10   11   12   13   14

    人的编号:   0    1    2    3    4    0    1    3    4     1    3     1     3     3     3

    我们知道第2、5、8、11、14个报数的要自杀。

    我们设第$x=a imes m+b(b<m)$次自杀的人的编号为$y$。

    $x$次报数后,一共死了$a=lfloor frac{x}{m} floor$人。

    如果$y$这个人没有在这次报数后自杀,那么他还需要等待剩下$n-a$人报数后才会再报数。

    即他下次报数将是 $t=x+n-a=a imes m+b+n-a=a imes (m-1)+b+n$ 时刻。

    那么如果我们知道这一次他报数的时刻$t$,反过来求上一次报数的时刻$x$呢?

    那就是:$x=t-n+a=t-n+lfloor frac{t-n}{m-1} floor$。

    我们知道如果知道第$k$个人最后一次报数的时刻,然后反着推他上一次报数的时刻,一直到时刻数$< n$(因为是从0开始的)的时候,时刻数就是他的编号。

    弱者就是会被欺负呀
  • 相关阅读:
    dijkstra算法模板 -- 最短路
    0-1背包
    POJ 1456-Supermarket(贪心+并查集)
    CodeForces 556C
    CodeForces
    POJ 2253-Frogger(Floyd变形)
    POJ 1251-Jungle Roads(最小生成树)
    HDU 1846-Brave Game(巴什博弈)
    HDU 1233-还是畅通工程(经典最小生成树)
    51Nod 1649-齐头并进(最短路dijkstra)
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/7638510.html
Copyright © 2011-2022 走看看