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 

  • 相关阅读:
    ural(Timus) 1019 Line Painting
    ACMICPC Live Archive 2031 Dance Dance Revolution
    poj 3321 Apple Tree
    其他OJ 树型DP 选课
    poj 3548 Restoring the digits
    ACMICPC Live Archive 3031 Cable TV Network
    递归循环获取指定节点下面的所有子节点
    手动触发asp.net页面验证控件事件
    子级Repeater获取父级Repeater绑定项的值
    没有列名的数据绑定
  • 原文地址:https://www.cnblogs.com/ToTOrz/p/6061403.html
Copyright © 2011-2022 走看看