对动态规划解法的思考过程(记录向)

现实问题:

  • 我们无法直接求得最后剩余的人

注意到:

  • 相邻两轮的关系我们知晓

所以:

为了用好这个关系,我们将其抽象为以下情景:

  • 以n = N 为先,重新编排这一轮的编号
  • 当它失去一个人的时候,我们会发现第N轮和第N-1轮的相互映射关系是固定的
  • 注意到,我们如果将第N - 1 单独作为之前的最后一轮,也从头开始编号,他们的关系就可以直接递推下去
  1. 下面n = 6,m = 2,k = 0为例(注意题目k >= 1), 答案是4,
  2. 假设最后留下的人对这一轮从头开始的编号是 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;
}