【HDU4841】圆桌问题

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=4841

题解

如果真的按照题目中给定 \(n\) 的范围,暴力模拟做法是不可行的,

然而貌似数据范围没有那么大,所以可以逐圈模拟标记坏人;

另外,对于经典的约瑟夫问题,只需求出最后活下来的人的编号,

是可以用递归的方法 \(O(n)\) 解决的;

我们假设 \(f(n, m)\) 表示 \(n\) 个人依次报数报出 \(m\) 则被杀死时,

最终活下来的人的编号,为方便讨论,从 \(0\) 开始编号,

先不妨假设 \(m <= n\),则第一轮报数,\(m - 1\) 被杀死,

\(m\) 可看成问题规模为 \(n - 1\) 时,编号为 \(0\) 的人,

那么在问题规模为 \(n\) 时最终活下来的人编号为 \(m + f(n - 1, m)\)

此外还需取模保证答案在 \([0, n)\)

如果 \(m > n\),那么第一个被杀死的人编号为 \(m \% n - 1\)

于是答案为 \(m \% n + f(n - 1, m)\)

整理后可知,\(f(n, m) = (m + f(n - 1, m)) \% n\)

而当 \(n = 2\) 时,若 \(m\) 为偶数则答案为 \(0\),若 \(m\) 为奇数则答案为 \(1\)

实际上,如果规定 \(n = 1\) 时,答案为 \(0\)(初始状态就剩下 \(0\) 一个人),

也能根据上述递归公式推出 \(n = 2\) 时的答案。

上述解法依然可以继续优化,

如果 \(m + f(n - 1, m)\) 不超过 \(n\),则不必取模,

那么我们其实可以通过递推的做法,每次从 \(1\)\(n\) 枚举问题规模,

如果不需要取模,则不必依次枚举,而是可以跳跃着增长问题规模,

具体来说,答案每次增加 \(m\),而问题规模每次增加 \(1\)

二者差距每次缩小 \(m - 1\),则可计算得出每次能直接增加的问题规模大小。

AC代码

点击展开
#include <cstdio>
#include <vector>

using namespace std;

vector<int> person;

int main() {
    int n, m;
    while (~scanf("%d%d", &n, &m)) {
        person.clear();
        for (int i = 0; i < 2 * n; ++i) person.push_back(i);
        int pos = 0;
        for (int i = 0; i < n; ++i) {
            pos = (pos + m - 1) % person.size();
            person.erase(person.begin() + pos);
        }
        for (int i = 0, j = 0; i < 2 * n; ++i) {
            if (i % 50 == 0 && i) puts("");
            if (j < n && i == person[j])
                printf("G"), ++j;
            else printf("B");
        }
        puts("\n");
    }
    return 0;
}

posted @ 2020-11-22 22:59  可爱的小凯凯  阅读(133)  评论(0)    收藏  举报