n个人围成一圈,从0开始报数,报到m-1人的退出,下一个从0开始,继续报到m-1的人退出。问最后剩下的人的序号。
输入:n和m
输出:剩下的人的序号
例如:
输入:10 2
输出:5
C++版本。
最容易想到的想法。先生成一个从1到n的数组a[n],存序号。循环遍历这个数组。用q表示剩下多少个人,用p表示计数循环(0,1,2…,n,n+1,…),如果序号为k的人退出,则设a[k]=0,用p计数时跳过等于0的人。
#include <iostream>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
int a[10000];
for(int i=0;i<n;i++){
a[i]=i+1;
}
int p=0; //记循环
int q=n; //记人数
for(int i=0;;i++){
if(i==n) i=0; //0,1,2,...,n-2,n-1,0,1,2...
if(a[i]!=0) p++; //计数
else continue; //跳过这个退出的人
if(p%m==0){ //报到m-1退出,第一个报的人的p是1,所以这个是p%m
a[i]=0;
q--;
}
if(q==1) break; //只剩一个人,退出
}
for(int i=0;i<n;i++){ //输出那个不为0的人
if(a[i]!=0) cout<<a[i]<<endl;
}
return 0;
}
还有链表的方法,再说吧
这是个约瑟夫环问题。公式法详解。
以8个人,报数到3的人退出为例:
1 2 3 4 5 6 7 8
4 5 6 7 8 1 2
7 8 1 2 4 5
2 4 5 7 8
7 8 2 4
4 7 8
4 7
7
经典的数学问题,有递推式 f(n,m)=(f(n-1,m)+m)%n
-f(n,m):表示有n个人,报到m的人退出,最终胜利者的编号。
-f(n-1,m):表示刚刚退出了一个人,有n-1个人,报到m的人退出,最终胜利者的编号。
以上面为例,8个人中3号退出,下面一个人成为头头,则后面的人相当于往前挪了3个单位。若已知n个人时,胜利者编号为f(n,m),那n-1个人的时候,胜利者编号往前挪了,则f(n-1,m)=f(n,m)-3。递归地逻辑一般是从1开始,所以反过来从小推出大的比较容易理解代码。f(n,m)=f(n-1,m)+3,又因为他是个环,所以%当前的人数,则f(n,m)=(f(n-1,m)+3)%n。
#include <iostream>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
int p =0;
for(int i=2;i<=n;i++){
p = (p+m)%i;
}
cout<<p+1<<endl; //从1开始给人编序号
return 0;
}
tql!