对动态规划解法的思考过程(记录向)
现实问题:
- 我们无法直接求得最后剩余的人
注意到:
- 相邻两轮的关系我们知晓
所以:
为了用好这个关系,我们将其抽象为以下情景:
- 以n = N 为先,重新编排这一轮的编号
- 当它失去一个人的时候,我们会发现第N轮和第N-1轮的相互映射关系是固定的
- 注意到,我们如果将第N - 1 单独作为之前的最后一轮,也从头开始编号,他们的关系就可以直接递推下去
- 下面n = 6,m = 2,k = 0为例(注意题目k >= 1), 答案是4,
- 假设最后留下的人对这一轮从头开始的编号是 num
轮数 | 编号 |
---|---|
N | 0 1 2 3 4 5 |
N-1 | 2 3 4 5 0 |
N-2 | 4 5 0 2 |
N-3 | 0 2 4 |
N-4 | 4 0 |
N-5 | 4 |
经过模拟,我们可以知道最后留下来的那个人在n = N-1的时候的从零开始的下标为:
num(N-1) = (N + (num(N) - m)) mod N
反推,得
num(N) = (num(N-1)+m) mod N
(别问我怎么反推的,我只会凭感觉😫😫😫)
显然,直接记录所有映射关系空间开销过大,
但是,题目只要求最后留下来那个人,所以我们不妨记其为 ans,
所以:
基于以上思路,我们记 n 个人时候答案为 ans = f(n),这里 n 从 0 ~ (n-1)编号,
那么由他们的映射关系我们可知 f(n) = (f(n-1) + m) % n,注意初始条件f(1) = 0;
由于题目多加了 k 的缘故,只需将开始的排列再与题目要求的排列 一一映射即可
即:
true_ans = (ans + k) % n;
正解:
void solve(){
int n, k, m;
cin >> n >> k >> m;
int fn = 0;// n = 1的解
for (int N = 2; N <= n;N++){
fn = (fn + m) % N;
}
cout << (fn + k) % n;
}