【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;
}

浙公网安备 33010602011771号