约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后 [1] 结果+1即为原问题的解。
有递推公式 f n = ( f n − 1 + k ) % n f_n=(f_{n-1}+k)\%n fn=(fn−1+k)%n, f n f_n fn表示n个人按k的取法最后一个人的编号,这个公式的证明:
首先我们是在知道 f n − 1 f_{n-1} fn−1的情况下,比如1 2 3 4 5 k=3,第一轮取了3,剩下编号是1 2 4 5,但是其实我们第二轮取的时候编号是从4开始编为1的,也就是3 4 1 2,对比这两个序列,可以看出这个老的序列( f n f_n fn)就是新序列( f n − 1 f_{n-1} fn−1)+k再取模n的
可以递归也可以不递归
int solve(int n,int k){
for(int i=1;i<=n;i++){
f[i]=(f[i-1]+k)%i;
}
return f[n]+1;
}
这个算是约瑟夫环的变形吧,就是每一轮删完不是从下一个删,而是从第一个开始,比如1-10,k=3,第一轮删除3,6,9之后,不是从10开始算1,而是从1开始,没有形成一个环
hdu2211
#include <bits/stdc++.h>
using namespace std;
int t;
long long n,k;
//函数返回的就是胜利者编号
long long cir(long long n,long long m){
//递归边界
if(n==k){
return k;
}
//一轮过去后剩下n-n/k人
//x就是下一轮也就是最后胜利者的编号
long long x=cir(n-n/k,k);
//在这一轮的编号t=(x-1)-(x-1)/k+1再化简
return (x-1)/(k-1)+x;
}
int main(void){
scanf("%d",&t);
while(t--){
scanf("%lld%lld",&n,&k);
printf("%lld\n",cir(n,k));
}
return 0;
}