luogu1996:约瑟夫问题:循环队列:模拟/链表

题目连接

  • 该题是luogu试炼场的2-13:T1

题目大意

  1. 给出一个环的规模n,第一次从1开始,踢走第m个人;
  2. 每次踢人之后,从下一个人开始操作,直到队列为空;
  3. 按顺序输出被踢出的人的编号。

题目分析

  • 循环队列的元问题,队列是什么?
  • 但题目涉及到,有人会被踢出,所以是一个中间断开的数组,不能用固定的队列来做。


解题思路1:暴力模拟

  1. 因为是元问题,数据只有100;
  2. 用bool数组来记录,第 i 个人是否还在队列内,暴力判断即可;

暴力模拟的代码:

//luogu1996:约瑟夫问题

//循环队列元问题 
//暴力模拟
 
#include
using namespace std;
 
const int mx=1e3+5;
int a[mx],n,m;
  
int main()
{
	scanf("%d %d",&n,&m);

	for(int i=1;i<=n;i++) a[i]=1;
	
	int t=n;
	int x=1;//当前的开头 
	
	while(t>0) //队列当前的规模 
	{
		for(int i=1;i<=m;i++)//询问 m次 
		{
			while(!a[x]) //跳过已经踢出队列的人 
			{
				x++;
				if(x>n) x=1;
			}
			x++;
			if(x>n) x=1;
		}

		//输出 
		if(x==1) 
		{	
			a[n]=0; printf("%d ",n); 
		}
		else
		{ 
			a[x-1]=0; printf("%d ",x-1);
		}
		
		t--; //队列规模减小 
	}

	return 0;
}


解题思路2:链表的使用

  1. 本题解用的是手动链表;如果有兴趣了解STL链表的同学:推荐看这篇
  2. f [i] 记录的是:i 的前一个人是谁(也称为父节点);
  3. s [i]记录的是:i 的下一个人是谁(也称为子节点);
  4. 每次剔除一个人的时候,将它的父子节点分别更新,则可以减少很多判断:
  5. 如图下图分析:
    luogu1996:约瑟夫问题:循环队列:模拟/链表_第1张图片

用双向链表的代码:

//luogu1996:约瑟夫问题

//循环队列元问题 
//链表跳转 
 
#include
using namespace std;
 
const int mx=1e3+5;
int f[mx],s[mx],n,m;
  
int main()
{
	scanf("%d %d",&n,&m);

	for(int i=0;i<=n;i++) //手动双向链表 
	{
		f[i]=i-1;//前驱 
		s[i]=i+1;//后继 
	}
	f[1]=n; s[n]=1;//循环 

	int t=n,x=0;
	while(t>0)//规模 
	{
		for(int i=1;i<=m;i++) x=s[x];//跳转询问 
		
		//更新 
		printf("%d ",x);
		s[f[x]]=s[x];//我ko了,所以我儿子的老爸,变成了我过去的老爸 
		f[s[x]]=f[x];//我老爸的儿子,变成了我过去的儿子 
		
		//缩小规模 
		t--;	
	}
		
	return 0;
}

你可能感兴趣的:(题解,题表,元问题,队列,luogu,大礼包)