• 北航算法作业一 约瑟夫环问题


    一.单向链表模拟

    class Node:
        def __init__(self, num, next):
            self.num = num
            self.next = next
    
    a = []
    # n个人
    n = 3
    # 数到k的人死去
    k = 2
    for i in range(0, n):
        a.append(Node(i, (i + 1) % n))
    # 计数器
    counter = 1
    # 现在的人
    now = a[0]
    while True:
        next = a[now.next]
        if next == now:#就剩下一个人了,游戏结束
            break
        else:
            counter += 1
            if counter == k:#计数器为k,要死人了
                counter = 0
                now.next = next.next
                print(str(next.num)+" has died",end='
    ')
            else:#不死人,now=next,向后走一格
                now=next
    #打印唯一的幸存者
    print(now.num)

    二.递推公式法

    f(n)=[f(n-1)+k]%n

    编号从0开始,f(n)表示n个人数到k的死去最后的幸存者,显而易见第一个死去的人是(k-1)%n,死者后面一人编号为0,再后面为1,以此类推,构成一个新的f(n-1).所以f(n)中的幸存者就是f(n)=[f(n-1)+k]%n.

    #n个人
    n=100
    a=[0]*n
    #数到k的人死去
    k=2
    a[1]=0
    for i in range(1,len(a)):
        a[i]=(a[i-1]+k)%i
    for i in range(1,len(a)):
        print(a[i],end=' ')

    输出为

    0 0 2 0 2 4 6 0 2 4 6 8 10 12 14 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 

    很显然可以找到规律,数字周期为第0个周期长度为1,第1个周期长度为2,第2个周期长度为4,第三个周期长度为8....对于数字n,它所在的周期为log2(n),数字n在周期中的列数为n-2^log2(n).

    当k=2时,f(n)=[n-2^log2(n)]*2

    验证上述结论:

    from math import  *
    for i in range(1,100):
        print((i-2**int(log2(i)))<<1)

    在上述表述中,一切计数都从0开始.从0开始,你会发现问题的表达形式会简单很多,从0开始计数确实应该成为一种习惯.

    三.k=2时的另一种递推公式

    f(2n)=2*f(n)

    f(2n+1)=f(n)+1

    下标一律从0开始,当有2n个人,数完一圈之后,奇数号人全跪了.给剩下的人重新编号,原来的0号还是0号,原来的2号变成1号,原来4号变成2号....即原来的2n号变成了现在的n号.剩下n个人的幸存者为第f(n)号,这个人对应原来的f(n)*2号.所以f(2n)=2f(n).

    当有2n+1个人,数完一圈之后,奇数号人全跪了,最后一个人2n号没有跪,下一个跪的是0号.这样第一圈算跪了n+1个人.对剩下的n个人重新编号,原来的2号变成0号,原来的4号变成1号,原来的6号变成2号,原来的8号变成3号....即原来的2n号变成了现在的n-1号.剩下n个人的幸存者为f(n)号,对应原来的(f(n)+1)*2号,所以f(2n+1)=2f(n)+2.

    对于这种问题,关键在于重新编号.

    四.比较快的递推公式法

    递推公式f(n)=(f(n-1)+k)%n,复杂度为O(n).有一个更快的递推式:

    f(n)=[f(n-n/k)+n-n%k+f(n-n/k)/(k-1)]%n

    f(n-n/k)第一轮输完之后,n/k个人死了,就剩下n-n/k个人.剩下的任务就是找到f(n-n/k)这个人对应f(n)中的哪个人.f(n-n/k)每(k-1)个人中可以插入一个死者,这些死者是要算数的.最后一个死者是n-n%k,他的下一个为0号,这是偏移量.这个递推公式复杂度

    f(n)=常数*f(n-n/k)

    复杂度为log(n)级别.

    五.关于约瑟夫环

    约瑟夫环花样繁多,比如给定一个数组

    1 4 当第一个人死去,下一个死去的人是第一个人左边的第4个人

    2 3 当2号人死去,下一个死去的人是第二个人左边第3个人

    3 4 当3号人死去,下一个死的人是他左边的第4个人

    ...

    给定一个数组,问最后的幸存者是谁.

    这个问题可以用线段树来求解.

  • 相关阅读:
    2018.12.29-dtoj-3626
    2018.12.28-bzoj-3784-树上的路径
    2018.12.28-bzoj-2006-[NOI2010]超级钢琴
    2018.12.28-dtoj-3648-寝室管理
    2018.12.27-dtoj-4089-line
    2018.12.27-dtoj-3151-相遇
    2018.12.25-dtoj-4086-针老师(truth) || dtoj-3657: 排列(permutation)
    不要62 hdu2089
    Kia's Calculation hdu4726
    The Moving Points hdu4717
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/5966204.html
Copyright © 2020-2023  润新知