zoukankan      html  css  js  c++  java
  • XidianOJ 1008,1009,1018 约瑟夫环问题汇总

    约瑟夫环问题:

      任给正整数n、k,按下述方法可得排列1,2,……,n的一个置换:将数字1,2,.. .,n环形排列,按顺时针方向从1开始计数;计满K时输出该为之上的数字(并从环中删去该数字),然后从下一个数字开始继续计数,直到环中所有数字均被输出为止。试编写一算法,对输人的任意正整数n、k,输出相应的置换。

    经典的问题,在数据较小的时候,直接模拟即可,O(nk)

      

    #include <stdio.h>
    
    int main(int argc,char** argv)
    {
            int n,k;
            scanf("%d %d",&n,&k);
    
            int i;
            int over[1001] = {0};
    
            int now = 0;
            for (i=1;i<=n;i++){
                int work = 0;
                while ( work < k ) {
                    now ++;
                    if (now > n) now = 1;
                    if (over[now]) continue;
                    work ++;
                }
                over[now] = 1;
                if (i == n){
                    printf("%d",now);
                    break;
                }
                printf("%d ",now);
            }
    
        return 0;
    }
    View Code

    当数据变大时,就需要使用更省时的算法,有O(nlogn)的,有O(nlogm)的,基本都是建树解决

    #include <stdio.h>
    #include <stdlib.h>
    #define MAXSIZE 200001
    
    typedef struct TreeNode* Tree;
    
    struct TreeNode {
        Tree Left,Right;
        int SIZE;
        int VALUE; 
    };
    
    Tree Forest[MAXSIZE],ROOT[MAXSIZE];
    int PREV[MAXSIZE],NEXT[MAXSIZE];
    int RESULT[MAXSIZE];
    int DEAD[MAXSIZE] = {0};
    int n,m;
    int Height;
    // helpers
    int MIN(int x,int y){
        if (x < y) return x;
        return y;
    }
    int CalHeight(){
        int i = 0,base = 1;
        while (base < m) {
            i ++; base *= 2;
        }
        return i+1;
    }
    int powOfTwo(int i){
        int j = 0,base = 1;
        while (j < i){
            j ++;
            base = 2 * base;
        }
        return base;
    }
    void PrintTree(int i,Tree T,int NodeNum){
        if (!T) return;
        if (T->VALUE == 0){
            //printf("%d %d SIZE=%d
    ",i,NodeNum,T->SIZE);
            PrintTree(i,T->Left,2*NodeNum);
            PrintTree(i,T->Right,2*NodeNum+1);
        }
        else printf("%d %d SIZE = %d %d
    ",i,NodeNum,T->SIZE,T->VALUE);
    }
    
    
    //
    int nowNum = 0;
    Tree makeTree(Tree T,int nowHeight,int i,int NodeNum){
        if (!T) {
            T = (Tree)malloc(sizeof(struct TreeNode));
            T->Left = NULL; T->Right = NULL; T->VALUE = 0; T->SIZE = 0;
            if (nowHeight == Height) {
                nowNum ++;
                if (nowNum > m*i|| nowNum > n) {
                    nowNum --;
                    return T;
                }
                T->VALUE = nowNum;
                return T;
            }
        }
        T->Left = makeTree(T->Left,nowHeight+1,i,2*NodeNum);
        T->Right = makeTree(T->Right,nowHeight+1,i,2*NodeNum+1);
        return T;
    }
    int AdjustTreeSize(Tree T){
        if (!T) return 0;
        if (T->VALUE != 0) {
            T->SIZE = 1;
            return T->SIZE;
        }
        int size = AdjustTreeSize(T->Left) + AdjustTreeSize(T->Right);
        T->SIZE = size;
        return size;
    }
    Tree createTree(int i){
        Tree root = NULL;
    
        
        
        
        root = makeTree(root,1,i,1);
        AdjustTreeSize(root);
        return root;
    }
    
    
    int LEAF1[MAXSIZE],LEAF2[MAXSIZE],total1,total2;
    void GETLEAF1(Tree T,int nowHeight){
        if (!T) return;
        if (nowHeight == Height){
            if (T->VALUE != 0)
            {
                total1 ++;
                LEAF1[total1] = T->VALUE;
            }
            return;
        }
        GETLEAF1(T->Left,nowHeight+1);
        GETLEAF1(T->Right, nowHeight+1);
        return;
    }
    void GETLEAF2(Tree T,int nowHeight){
        if (!T) return;
        if (nowHeight == Height){
            if (T->VALUE != 0)
            {
                total2 ++;
                LEAF2[total2] = T->VALUE;
            }
            return;
        }
        GETLEAF2(T->Left,nowHeight+1);
        GETLEAF2(T->Right,nowHeight+1);
        return;
    }
    
    int LEAFRES[MAXSIZE],RESNOW = 0,total = 0;
    
    Tree InsertElementIntoT(Tree T,int nowHeight){
        if (!T) return NULL;
        if (nowHeight == Height){
            RESNOW ++;
            if (RESNOW > total) {
                RESNOW --;
                return T;
            }
            T->VALUE = LEAFRES[RESNOW];
            return T;
        }
        T->Left = InsertElementIntoT(T->Left, nowHeight+1);
        T->Right = InsertElementIntoT(T->Right, nowHeight+1);
        return T;
    }
    
    Tree Combine(int t){
        Tree T = ROOT[t],T1 = ROOT[NEXT[t]]; // make t and t1 a new t
        
        total1 = 0; total2 = 0; RESNOW = 0;
        GETLEAF1(T,1);
        GETLEAF2(T1,1);
        
        int i;
        total = 0;
        for (i=1;i<=total1;i++){
            total++;
            LEAFRES[total] = LEAF1[i];
        }
        for (i=1;i<=total2;i++){
            total++;
            LEAFRES[total] = LEAF2[i];
        }
        
        T = InsertElementIntoT(T,1);
        AdjustTreeSize(T);
        
        NEXT[t] = NEXT[NEXT[t]];
        PREV[NEXT[t]] = t;
        return T;
    }
    
    int main(int argc,char** argv)
    {
        
        scanf("%d %d",&n,&m);
        int i;
        
        Height = CalHeight();
        // buildTree
        int num = 0;
        if ( n > n/m*m){
            num = n/m + 1;
        }
        if ( n ==n/m* m){
            num = n/m;
        }
        for (i=1;i<=num;i++) {
            ROOT[i] = createTree(i);
            PREV[i] = i-1; NEXT[i] = i+1;
            if (i == num) NEXT[i] = 1;
            if (i == 1) PREV[i] = num;
           // printf("%d %d %d
    ",i,NEXT[i],PREV[i]);
           // PrintTree(i,ROOT[i],1);
        }
        
        int t = num,COUNT = 0,NXTCOUNT,temp = 0;
        for (i=1;i<=n;i++){
            if (t == NEXT[t]){
                while (COUNT < m){
                    COUNT = COUNT + ROOT[t]->SIZE;
                }
            }
            else
                while (COUNT < m){
                    t = NEXT[t];
                    COUNT = COUNT + ROOT[t]->SIZE;
                }
            NXTCOUNT = COUNT - m; temp = 0;
            Tree NODE = ROOT[t];
            while (NODE->VALUE == 0){
                NODE->SIZE --;
                if ((COUNT - NODE->Right->SIZE) < m){
                    NODE = NODE->Right;
                }
                else {
                   // printf("%d %d
    ",COUNT-NODE->Right->SIZE,m);
                    COUNT = COUNT - NODE->Right->SIZE;
                    NODE = NODE->Left;
                }
            }
            NODE->SIZE = 0;
            RESULT[i] = NODE->VALUE;
            NODE->VALUE = 0;
            
            // combine section
            COUNT = NXTCOUNT;
            if (t != NEXT[t]){
                if (ROOT[t]->SIZE + ROOT[NEXT[t]]->SIZE == m){
                    COUNT = COUNT + ROOT[NEXT[t]]->SIZE;
                    ROOT[t] = Combine(t);
                  //  PrintTree(t, ROOT[t],1);
                }
    //            else if (ROOT[t]->SIZE + ROOT[PREV[t]]->SIZE == m){
    //                t = PREV[t];
    //                ROOT[t] = Combine(t);
    //              //  PrintTree(t, ROOT[t],1);
    //            }
            }
        }
        
        for (i=1;i<=n;i++){
            if (i == n){
                printf("%d",RESULT[i]);
                break;
            }
            printf("%d ",RESULT[i]);
        }
        
        return 0;
    } 
    View Code

    1018只要求输出前几个数,所以可以使用一个很巧妙的函数来做到

    long kth(long n, long m, long k)
    {
      for (k *= m; k > n; k = k-n+(k-n-1)/(m-1));
      return k;
    }

    因为第k个出列的人,肯定是在第mk次报数出列,通过倒推来算出最初的位置。

    #include <stdio.h>
    
    
    
    long kth(long n, long m, long k)
    {
      for (k *= m; k > n; k = k-n+(k-n-1)/(m-1));
      return k;
    }
    
    int main(int argc,char** argv)
    {
        long n,k,t,i;
        while (scanf("%ld %ld %ld",&n,&k,&t) == 3){
          long q[100] = {0};
          for (i=0;i<t;i++) {
            scanf("%ld",&q[i]);
          }
          for (i=0;i<t;i++){
            if (i == t-1){
              printf("%ld
    ",kth(n,k,q[i]));
              break;
            }
            printf("%ld ",kth(n,k,q[i]));
          }
        }
        return 0;
    }
    View Code

    参考资料

      http://maskray.me/blog/2013-08-27-josephus-problem-two-log-n-solutions

      An O(n log m) Algorithm for the Josephus Problem 

  • 相关阅读:
    json.dumps loads 终于区分出来了
    os.mkdir()和os.path.join()的区别
    readline与readlines之间的简单区别
    python:extend (扩展) 与 append (追加) 之间的天与地
    论raw_input与input之间的缠缠绵绵
    python-set
    HDU4343Interval query 倍增
    HDU2586How far away? LCA
    LCA算法解析-Tarjan&倍增&RMQ
    POJ1330Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/ToTOrz/p/6061403.html
Copyright © 2011-2022 走看看