XidianOJ 1008,1009,1018 约瑟夫环问题汇总

约瑟夫环问题:

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

 

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

  

《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》
《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》

#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)的,基本都是建树解决

《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》
《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》

#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\n",i,NodeNum,T->SIZE);
        PrintTree(i,T->Left,2*NodeNum);
        PrintTree(i,T->Right,2*NodeNum+1);
    }
    else printf("%d %d SIZE = %d %d\n",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\n",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\n",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次报数出列,通过倒推来算出最初的位置。

《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》
《XidianOJ 1008,1009,1018 约瑟夫环问题汇总》

#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\n",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 

    原文作者:约瑟夫环问题
    原文地址: https://www.cnblogs.com/ToTOrz/p/6061403.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞