约瑟夫环问题
假设有 个数,编号为 ,其中0和 相连,形成一个环,如图:
从0开始数
个,将这个数从环中移除,再数
个,再移除,问最后一个数是多少。
假设
。那么第一次删除2,数列变成:
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
0 | 1 | 3 | 4 | 5 | 6 |
此时,1和3分离开了,为了使1和3连续,我们可以将列表重新编号,从被删除的下一个数开始编号,即:
\ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
编号前 | 0 | 1 | 3 | 4 | 5 | 6 | |
编号后 | 4 | 5 | 0 | 1 | 2 | 3 |
这样新表的下一个要删除的数编号为2,而旧表中为5。
不难发现,新旧表的下一个待删除的数的编号存在这样一个关系:
根据模数的性质,可以推出:
那么由6个数删到一个数的数列变化:
n | \ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|---|
7 | - | 0 | 1 | 2 | 4 | 5 | 6 | |
6 | 编号前 | 0 | 1 | 3 | 4 | 5 | 6 | |
6 | 编号后 | 4 | 5 | 0 | 1 | 2 | 3 | |
5 | 编号前 | 4 | 5 | 0 | 1 | 3 | ||
5 | 编号后 | 1 | 2 | 3 | 4 | 0 | ||
4 | 编号前 | 1 | 3 | 4 | 0 | |||
4 | 编号后 | 3 | 0 | 1 | 2 | |||
3 | 编号前 | 3 | 0 | 1 | ||||
3 | 编号后 | 0 | 1 | 2 | ||||
2 | 编号前 | 0 | 1 | |||||
2 | 编号后 | 0 | 1 | |||||
1 | 编号前 | 1 | ||||||
1 | 编号后 | 0 |
记
为
时的编号后的数列,
为最后被删除的数在
中的编号,则:
并且有:
那么有:
求出了结果为3,与表格中的一致。
回归题目
而题目中是从m开始,而非从第一个开始。因为从第 个开始移除,那么接下来会从0开始编号,那么从 开始的话接下来会从 开始编号,并且题目从1开始编号,因此答案还要加上 。有因为 答案 有可能小于等于0,因此再判断一下即可。
#include<iostream>
using namespace std;
int main() {
int n, k, m;
while (true) {
cin >> n >> k >> m;
if (!n && !k && !m) {
break;
}
int ans = 0;
for (int i = 2; i <= n; ++i) {
ans = (ans + k) % i;
}
ans = (m - k + 1 + ans) % n;
if (ans <= 0) {
ans += n;
}
cout << ans << endl;
}
}