zoukankan      html  css  js  c++  java
  • 循环链表范例Josephus问题

      一群小孩围成一圈,任意假定一个数N,从第一个小孩起,逆时针方向数,每数到第M个小孩时,该小孩就离开,然后再由下一个重新报数,小孩不断离开,圈子不断缩小。最后,剩下的一个小孩便是胜利者。究竟胜利者是第几个小孩呢?

       类似这样的问题都叫Josephus(约瑟夫斯)问题。我们可以用一个循环链表表示排成圆圈的人,整数i代表第i个人,先为1号建立一个单节点循环链表,再把2号到N号依次插入到1号节点后面,这样就形成了一个由1--N(逆时针)的员,最后x指向N,然后我们从1号开始跳过M-1个节点,把第M-1个节点的链接改为指向第M+1号节点,一直这样做,直到只剩下一个节点为止。

    #include<iostream>
    using namespace std;
    typedef int T;
    struct node
    {
        T data;
        struct node *next;
        node(T x,node *t):data(x),next(t) { }
    };
    typedef struct node *Link;
    
    
    int main()
    {
        int i, N,M;
        cin>>N>>M;
        Link t=new node(1,0); 
        t->next=t;
    
        Link x=t;
        for(i=2;i<=N;i++)
            x=(x->next=new node(i,t));
        while(x!=x->next)
        {
            for(i=1;i<M;i++)  x=x->next;
            x->next=x->next->next;
        }
        cout<<x->data<<endl;
    }

     递推思路:

    本思路可用O(n)的复杂度解决最原始的Josephus问题,即只找出最后一个幸免者。关于约瑟夫(Josephus)问题:

    N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。最后剩下1号。

    为了方便使用mod处理数据,将N个人编号0-N-1,每次报数从0开始,报到M-1的被杀掉。

    第一个被杀掉的一定是编号(M-1)mod(N)的人,他被杀掉后,令k=(M)mod(N),则剩下的问题为:编号为:k,k+1,k+2,…,n-1,0,1,…,k-2的N-1个人,从k开始从0进行报数,报到M-1的人被杀掉。所以如果做一个i’ = (i-k)mod(N)的映射,就可以真正将问题转换成J(N-1,M),解出答案后再做逆映射i = (i’+k)mod(N)即可得到J(N,M)的解,即J(N,M) = (J(N-1,M)+k)mod(N)。又知道k = (M)mod(N),所以递推公式的形式是J(N,M) = (J(N-1,M)+M)mod(N)。递推起点是J(1,M)=0。(注意k,k+1,k+2,…n-1,0,1,…k-2的序列在考虑mod操作后并没有数据上的跳跃,因此做了映射后才仍然是问题的原型,抹去了杀掉一个人的影响。)

    J(1,M) = 0
    
    J(N,M) = (J(N-1,M)+M)mod(N)

    这样,只要从1推到N就可以得出问题的答案,复杂度O(N)。

    深入:

    http://blog.pfan.cn/xiangyu/3527.html

     

    用数学方法解的时候需要注意应当从0开始编号,因为取余会等到0解。

    实质是一个递推,n个人中最终存活下来的序号与n-1个人中存活的人的序号有一个递推关系式。

    分析:

    假设除去第k个人。

    0, 1, 2, 3, ..., k-2, k-1, k, ..., n-1  //original sequence (1)

    0, 1, 2, 3, ..., k-2,      , k, ..., n-1  //get rid of kth person (2)

    k, k+1, ..., n-1,    0,    1,        ..., k-2  //rearrange the sequence (3)

    0, 1,     ..., n-k-1, n-k, n-k+1, ..., n-2  //the n-1 person (4)

    我们假设f(n)的值为n个人中最后存活的人的序号,则

    注意到(2)式(3)式(4)式其实是同一个序列。

    注意(1)式和(4)式,是同一个问题,不同的仅仅是人数。

    假设我们已知f(n-1),即(4)式中最后剩下的人的序号,则(3)式所对应的序号,就是f(n),即(1)式n个人中最后存活的序号。

    而从(3)(4)式中我们不难发现有这样一个递推式:

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

    显然,f(1) = 0。

    于是递推得f(n)

    #include <stdio.h>
    
    int main()
    {
        int n, k, m, i, x;
        while (scanf("%d%d%d", &n, &k, &m) != EOF) {
        if (n==0 && k==0 && m==0) break;
        x = 0;
        for (i=2; i!=n; ++i)
            x = (x + k) % i;
        x = (x + m) % i + 1;
        printf("%d\n", x);
        }
        return 0;
    }
  • 相关阅读:
    运行Jmeter时,出现java.util.prefs.WindowsPreferences <init>
    Jmeter下载安装及JDK安装
    面试常用-基础理论(五)
    Fiddler (六) 最常用的快捷键
    Fiddler (五) Mac下使用Fiddler
    Fiddler (四) 实现手机的抓包
    Fiddler (三) Composer创建和发送HTTP Request
    Fiddler (二) Script 用法
    Web调试工具——Fiddler介绍
    生成任意位数随机字符串
  • 原文地址:https://www.cnblogs.com/youxin/p/2612603.html
Copyright © 2011-2022 走看看