题目描述
周末了,软件ACM的队员准备玩玩游戏,娱乐一下,CY想了一个好主意,所有队员站成一个圈,从1开始报数,凡是报出指定数字的人要出列,有人出列后,下个人重新从1开始报数。最后一位“幸存者”要给大家表演个节目。由于队员正在不断的壮大,C小加又想知道他是否需要准备表演,请你设计个程序,帮他确定他是否是“幸存者”。
输入
第一行是n,有n次游戏,第二行是m,x,表示某次游戏有m个人,指定被请出列的数字为x。其中n<100,m<1000
输出
最后幸存的那位的原来的号码
样例输入
2 10 5 6 4
样例输出
3 5
分析:这是一道约瑟夫环问题,我们可以利用两种方法来解决此题
方法一(公式递推法)
具体方法请参阅:https://blog.csdn.net/u011500062/article/details/72855826
这种方法就是从最后的一种情况进行递推
每杀掉一个人,下一个人成为头,相当于把数组向前移动M位。若已知N-1个人时,胜利者的下标位置位f(N−1,M),则N个人的时候,就是往后移动M为,(因为有可能数组越界,超过的部分会被接到头上,所以还要模N),既f(N,M)=(f(N−1,M)+M)%n每杀掉一个人,其实就是把这个数组向前移动了M位。然后逆过来,就可以得到这个递推式。
这种递推方法稍难理解,但不耗时间,不过只能输出最后一个要站出来的人。如果要挨个输出所有出来的人就没办法了。
#include<stdio.h>
int main()
{
int t,m,n;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
int f=0;
for(int i=2;i<=n;++i)//从最后一个人开始递推
f=(f+m)%i;
printf("%d\n",f+1);
}
}
方法二:队列
我们可以把报数情况看成是一个队列,队列头的人报完数就出队列回到队列尾,报到第x个人时,直接出列即可。这种情况情况容易理解,但略耗时间
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
const int M=1e3+5;
int i,j,k,n,m,x,y,t,temp;
int main()
{
scanf("%d",&y);
while(y--)//输入测试数据次数
{
queue<long long>q;
scanf("%d%d",&m,&x);//读入数据m和x
for(i=1;i<=m;i++)//将前m个数依次放入队列中
q.push(i);
j=0;
while(!q.empty())//当队列不为空就一直循环
{
if(++j==x)//如果报到了第x个人,此人出列,并记录编号
{
temp=q.front();
q.pop();
j=0;
}
else//否则先出列,再回到队尾
{
t=q.front();
q.pop();
q.push(t);
}
}
printf("%d\n",temp);
}
}