约瑟夫环的两种实现方式 - 模拟 and 队列

先讲一下约瑟夫环的模拟实现方式,与队列相比复杂度较低。先给个题 ~ 让大家了解一下什么是约瑟夫环 ~

codeVS 1282:

有编号从1NN个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字M时就出圈。直到只剩下1个小朋友,则游戏完毕。现在给定N,M,求N个小朋友的出圈顺序。

这就是约瑟夫环 ~

本人AC代码:

//约瑟夫环 - 模拟
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 3e4 + 7;
int n, m;
int a[maxn];

int main() {
    scanf("%d %d", &n, &m);
    int t = 1;
    for(int i = 1; i <= n; i++) a[i] = i;
    int tmp;
    while(n) {
        tmp = (t + m - 1) % n;
        if(tmp == 0) tmp = n; //正好是末尾元素
        printf("%d ", a[tmp]);
        for(int i = tmp; i < n; i++) a[i] = a[i + 1];
        n--;
        t = tmp;
    }

}

再来一道进阶的,不是从1开始报数的,而是任意位置,这就要用到队列,但是复杂度高,适合小数据范围,1e4以上可能会tle

题目链接:https://vjudge.net/problem/OpenJ_Bailian-3254

思路:

以题目样例为例:

因为先从初始位置开始报数,所以先将初始位置pos ~ n 压进队列,再将1 ~ pos - 1 压进队列

然后手动模拟一下,维护一个队头,等到走到报的数需要出局的时候,就取当前队头打印,然后pop出去,以此类推。

本来想传图,但每次传图都传不上去,大家可以按照我的思路,手动模拟一下。

本人AC代码:

//约瑟夫环 - 队列
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int n, p, d;
map mp;
queue qua;

int main() {
    while(scanf("%d %d %d", &n, &p, &d) != EOF) {
        if(!n && !p && !d) break;
        while(!qua.empty()) qua.pop(); //多组清空队列
        for(int i = p; i <= n; i++) qua.push(i);
        for(int i = 1; i < p; i++) qua.push(i);
        while(qua.size() > 1) {
            for(int i = 0; i < d - 1; i++) {
                int t = qua.front();
                qua.pop();
                qua.push(t);
            }
            printf("%d,", qua.front());
            qua.pop();
        }
        printf("%d\n", qua.front());
    }
    return 0;
}

你可能感兴趣的:(水题合集)