问题描述:N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数。问最后剩下的人的编号。
例如:N = 3,K = 2。2号先出列,然后是1号,最后剩下的是3号。
最直接的方法是暴力枚举法,第一一个标志数组label[n],初始化为0,当label[i]等于1时表示对应编号为i的人已经出列,直到最后一个人出列。
部分代码如下:(程序中将编号设定为0~n-1),便于理解。
#include<iostream>
#include<algorithm>
using namespace std;
int label[50]={0};
int main()
{
int N,k,final;
cin>>N>>k;
int m=N;
int count=0;
while(m)
{
for(int i=0;i<N;i++)
{
if(!label[i])
count++;
if(count==k)
{
label[i]=1;
count=0;
m--;
final=i;
}
}
}
cout<<final+1;
}
暴力枚举法容易理解,但当N或K相差太大时,暴力枚举法就显得效率特别低。
现在换一种方式解决这个问题
假设现在有41人参与这个游戏,计数为3的出列
最开始时有这么多人
0 1 2 3 4 5 6 7 8 9···················39 40
第一次出列,则是(3-1)%41=2出列,现在的情况是
0 1 3 4 5 6 7 8 9····························39 40
第一次出列后又重新从3%41=3开始计数,想当于上一步的0,那么可以将3当做头,既然是头那么就设置为0,则可以表示为
-3 -2 -1 0 1 2 3 4 5 6 ···························37
出现了负数,不便于计算,则给负数加上总人数(相当与接在最后一个人后面,实现了真正的环),表示为
38 39 40 0 1 2 3 4 5 6·····················37
这样就变成了一个40人的游戏,依次类推,第二次出列的是(3-1)%40=2号,对应的是第一步的(2+3)%41=5号
接下来出列的是(3-1)%39=2号,对应第二步的(2+3)%40=5号,对应第一步的(5+3)%41=8号
由此可得当人数为1时,胜利者为0号,那么根据以上推导公示推导为人数为n时胜利者的序号(实际序号只需要加1即可)
非递归代码如下
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{ int N,k;
cin>>N>>k;
int last=0;
for(int i=2;i<=N;i++)
{
last=(last+k)%i;
}
cout<<last+1;
}