約瑟夫問題是個有名的問題:N個人圍成一圈,編號由0到N-1,從第一個開始報數,第K個將出局,最后剩下一個人。例如N=6,K=5,出局人編號的順序是:4,3,5,1,2,贏家是0。
這里給出一個十分簡潔的遞歸解法:
#include <bits/stdc++.h>
int solve(int n, int k) {
if (n == 1) return 0;
else return (solve(n - 1, k) + k) % n;
}
int main() {
int N, K;
scanf("%d %d", &N, &K);
printf("%d", solve(N, K));
return 0;
}
理解:
solve(n,k)的意思是,有n人,以k為步長刪人的約瑟夫問題的解。那么顯然solve(1,k)=0,即只有1個人的時候,勝者就是編號為0的這一個人。
若不是1個人,有n人,刪掉第k人(編號k-1)之后變成了n-1人,下次起始的地方是從這個出局的人后面開始算的,不妨把該開始位置的人編號“看成“0。
示意:
... [k-1 ] [ k ] [k+1] ... // 刪之前 即“先狀態(tài)”
... [dele] [ 0 ] [ 1 ] ... // 刪之后 即“后狀態(tài)”
那么若要從n-1個人的“后狀態(tài)”恢復到“先狀態(tài)”,其實所有人的編號相當于加k,亦即f[n] = f[n-1] + k,再由于是環(huán)的問題,所以加完再對"先狀態(tài)時,即刪前"人數n取模即可。
這里有個小地方要注意一下,每次取模的是當前游戲人數,可以稍微想想為啥?如果你寫成迭代版本,以i為下標迭代,F[1]=0;F[i]=(F[i-1]+k)%i;這里是模i而非n,要留意。