队列及其应用-取牌游戏

队列及其应用-取牌游戏

一:基本概念
队列是一种操作受到限制的特殊线性表。其插入操作限定在表的一端进行,称为“入队”;其删除操作则限定在表的另一端进行,称为“出队”。插入一端称为队尾(rear);删除一端称为队头(front)。

在这里插入图片描述
队列也被称作“先进先出”线性表(FIFO,First In First Out)。类似于生活中排队购票,先来先买,后来后买。
在不断入队、出队的过程中,队列将会呈现出以下几种状态:
队空:队列中没有任何元素。
队满:队列空间已全被占用。
溢出:当队列已满,却还有元素要入队,就会出现“上溢(overflow)”;当队列已空,却还要做“出队”操作,就会出现“下溢(underflow)”。两种情况合在一起称为队列的“溢出”。

二:队列的基本操作
(1)初始化
使用数组实现队列时,初始状态为front=0,rear=0.表示队列里没有任何元素。如果有一个元素,则front=0,rear=1。

void clear(){
	front=rear=0;
}

(2)判空

bool empty(){
	if(front==rear)return 1;
	else return 0;
}

(3)求队列中实际元素的个数

int size(){
	return (rear-front);
}

4)入队
入队操作前,需要判断队列是否已满。

void push(int x){
	q[++rear]=x;
}

(5)出队

void pop(){
	front++;
}

(6)取队首元素

int get_front(){
	return q[front+1];
}

三:循环队列
随着入队与出队操作的不断进行,队头指针在数组中不断向队尾方向移动,而在队头前面产生了一片不能利用的“空闲区”,当队尾指针指向数组最后一个位置,即rear = maxn时,如果再有元素入队就会出现“溢出”,这种溢出称作“假溢出”。
如何解决这种情况呢?一种方法是每次出队操作时,都向“空闲区”整体移动一位,带来的后果是时间复杂度高了;另一种方法是让数组首尾相连,形成“环”状,即所谓的“循环队列”。
循环队列初始时,front = rear = 0,如果 maxn 个元素一个个依次入队,则 rear = maxn,此时再有元素入队,则它会被存放在 q[0] 这个单元,也会出现 front = rear = 0,与队空时的状态一样。解决方法是少用一个元素空间,约定数据入队前,测试“队尾指针在循环意义下加 1 后是否等于头指针”作为判断“队满”的条件。循环队列的实际长度为 (rear - front + maxn) % maxn。
循环队列的重要操作修改如下(使用 q[0] 这个单元):
(1)判断队满:如果(rear + 1) % maxn = front,则队列已满。
(2)入队:如果队列未满,则执行:rear = (rear + 1) % maxn;q[rear] = x;
(3)出队:如果队列不为空,则执行:front = (front + 1) % maxn;

四:取牌游戏
【问题描述】
小明正在使用一堆共 K 张纸牌与 N-1 个朋友玩取牌游戏。其中,N≤K≤100000,2≤N≤100,K 是 N 的倍数。纸牌中包含 M=K/N 张“good”牌和 K-M 张“bad”牌。小明负责发牌,他当然想自己获得所有“good”牌。
他的朋友怀疑他会欺骗,所以他们给出以下一些限制,以防小明耍诈:
1)游戏开始时,将最上面的牌发给小明右手边的人。
2)每发完一张牌,他必须将接下来的 P 张牌(1≤P≤10)一张一张地依次移到最后,放在牌堆的底部。
3)以逆时针方向,连续给每位玩家发牌。
小明迫切想赢,请你帮助他算出所有“good”牌放置的位置,以便他得到所有“good”牌。牌从上往下依次标注为 #1,#2,#3,…
【输入格式】
第 1 行,3 个用一个空格间隔的正整数 N、K 和 P。
【输出格式】
M 行,从顶部按升序依次输出“good”牌的位置。
【输入样例】
3 9 2
【输出样例】
3
7
8
代码:

#include<iostream>
using namespace std;

//定义数组最大长度 
const int MAXN = 100010;
//定义循环队列数组a 
int a[MAXN];
//定义结果数组 
int result[MAXN];
int main()
{
	//K:纸牌张数,N:人数,P:每次向底部放置的牌数 
	int K, N, P;
	//定义队首为a[1],舍弃a[0] 
	int front = 1;
	cin >> N >> K >> P;
	//队尾为a[K] 
	int rear = K;
	//将牌的大小存入相应的数组元素中 
	for (int i = 1; i <= K; i++)a[i] = i;
	// 小明在N的整数倍时得到自己的牌,x用来计数 
	int x = 0;
	//从最小的牌到最大的牌循环遍历 
	for (int i = 1; i <= K; i++)
	{
		//每发一个人x++ 
		x++;	
		//如果发到小明,则将当前队首元素存入结果数组中,桶排序方法 
		if (x % N == 0)result[a[front]] = a[front];
		//每次发完牌后,舍弃掉 
		a[front] = NULL;
		//指向下一张牌 
		front = (front + 1) % MAXN;
		//如果队首元素等于队尾元素加1,代表队空,与上面的定义有点不一样 
		if (front == rear + 1)break;
		//循环将P张牌放到队尾 
		for (int j = 1; j <= P; j++)
		{
			//队尾+1 
			rear = (rear + 1) % MAXN;
			//将队首的放入队尾 
			a[rear] = a[front];
			//队首清空 
			a[front] = NULL;
			//队首后移 
			front = (front + 1) % MAXN;
		}
	}
	//打印输出,桶排序方法 
	for (int i = 1; i <= K; i++) { if(result[i])cout << result[i] << endl; }

	return 0;

}

运行结果:

在这里插入图片描述
参考:《信息学奥赛课课通(C++)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值