对于约瑟夫问题,假设一开始有 n 个人,每个 k 个人图图掉一个,我们两种常见的暴力做法是数组模拟和链表模拟,时间复杂度前者是 O(nlogkn),后者可以达到 O(nk)。
具体数学对 k=2 的情况给出了一种构造,就是位移,将二进制数的第一位移到最后,形成的新二进制数就是最后剩下人的编号,时间复杂度 O(logkn),对于 k=3 的情况,根据尝试没有这种做法,于是另寻他路。考虑第一次图图掉第 k 个人,相当于从 k 位置开始处理 n−1 人的情况,于是我们有以下递推公式:
fn=(fn−1+k)%n
这样的时间复杂度是 O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13
#include<bits/stdc++.h> usingnamespace std; intmain() #define int long long { int n; scanf("%lld",&n); int ans=0; for (int i=1;i<n;i++){ ans=(ans+3)%i; } printf("%lld\n",(ans+3)%n+1); }
#include<bits/stdc++.h> usingnamespace std; intmain() #define int long long { int n; scanf("%lld",&n); int ans=0,cnt=0; for (int i=1;i<n;cnt++){ if (i-1-ans>=3){ int to=min(n-1,i-1+(i-1-ans)/3); int step=to-(i-1); ans=(ans+3*step)%to; i=to; } else { ans=(ans+3)%i; } i++; } printf("%lld\n",(ans+3)%n+1); }