NYOJ-排队

题目描述

周末了,软件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);
	}
}

猜你喜欢

转载自blog.csdn.net/basketball616/article/details/86636005
今日推荐