Josephus Problem
Leetcode 1823. 找出游戏的获胜者, 经典约瑟夫问题, 大致意思是: “N个人围成一圈, 第一个人从1开始报数, 报M的将被杀掉, 下一个人接着从1开始报. 如此反复, 最后剩下一个, 求最后的胜利者”. 如果知道递归公式就可以在 $O(N)$ 的时间内完成计算.
队列模拟
队首出列进队尾, 重复 k - 1 次后取出队首, 时间复杂度 $O(NK)$, 要是两者乘积小于 1e9 的话应该都是能过的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int findTheWinner(int n, int k)
{
queue<int> q;
for(int idx = 1; idx <= n; ++idx)
q.push(idx);
while(q.size() > 1)
{
for(int idx = 1; idx < k; ++k)
q.push(q.front()), q.pop();
q.pop();
}
return q.front();
}
};
递归公式
我们可以根据函数写出这样的递归形式, 即每次递归问题规模减一, 把数组看成循环数组, 每个点的索引值相较于上一轮都前进了 k.
1
2
3
4
5
6
7
8
9
10
class Solution {
public:
int findTheWinner(int n, int k)
{
if(n == 1)
return 1;
int res = (findTheWinner(n - 1, k) + k) % n;
return res == 0 ? n : res;
}
};
写出递归式: $F(n, k) = (F(n - 1, k) + k) \% n; F(1, k) = 1$
1
2
3
4
5
6
7
8
9
10
11
class Solution {
public:
int findTheWinner(int n, int k)
{
// 从 0 开始方便取模
int pos = 0;
for(int idx = 2; idx <= n; ++idx)
pos = (pos + k) % idx;
return pos + 1;
}
};
This post is licensed under CC BY 4.0 by the author.