DS homework-队列

本文探讨了YH学长如何利用酒馆战旗中的三连机制最大化队伍实力,同时面对JC学长的干扰,通过高效算法计算最终需要卖出的金色随从数量。关键在于巧妙地利用队列和标记技巧,降低时间复杂度,以求在限制队伍容量下赢得比赛。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

优质的随从慕名而来

题目

Description

YH 学长想在酒馆战旗中赢得第一名,因此他要找 Bob 用漏斗蛋糕招揽一些优质的随从!

有 n 个随从慕名而来,第 i 个随从的种类是 a_i。YH 学长十分贪心,他每个随从都想要,因此他准备了充足的黄金铸币并依次买下每一个随从。但即使如此,他也只能携带 m 个随从,当新买的随从无法加入队伍时,YH 学长只好卖掉队伍里之前最早买下的随从。

“啊哈,三连!”,机智的 YH 学长发现利用酒馆中的三连机制能够让随从们变得更加强大。当 YH 学长买下一个种类为 x 的随从时,若队伍里有另外两个随从种类也为 x ,他就凑成了一个“三连”。此时“三连”会将队伍中两个种类为x的随从移除,然后使买下的那个随从变为“金色”并加入队伍。

但为了防止 YH 学长的队伍变得太强,每当 YH 学长获得“金色”随从时,JC 学长就会偷偷卖掉这个”金色“随从。JC 学长想知道最后他需要卖掉多少 YH 学长的”金色“随从。

Input
第一行包含两个正整数 n,m(1<=m<=n<=1e6),表示随从的个数和队伍的容量。

第二行包含n个整数 ai(1<=ai<=1e6),表示第i次买下的随从的种类。

Output
输出一个整数,表示JC学长卖出的“金色”随从的个数

样例
input

10 3
2 1 1 1 2 3 3 1 3 1

output

2

分析:

通过分析本题可知,对于本题若采用满足题意(三连)直接删除的方式很难实现,所以可以采用构造一个队列的方式,当有满足题意(三连)的时候,将队列中的要删除的元素先标记,当队列已满要移动队列的时候,对于打上标记的元素在来考虑其直接出队的问题,这样就避免了有三连情况时,要直接删除元素的烦恼。
TLE code:
利用tt,hh,q[N] 模拟队列

#include<iostream>
using namespace std;
const int N = 1000100;
int tt,hh,q[N],num[N],ans,sign;
int main(){
	int n,m;
	cin >> n >> m;
	for(int i=0;i<n;i++){
		int x;
		cin >> x;
		num[x]++;
		q[tt++]=x;
		if(num[x]==3){
			ans++;
			num[x]-=3;
			for(int j=hh;j<tt;j++)       //打上标记的步骤
				if(q[j]==x) q[j]=-1;
			sign+=3;
		}
		if(hh-tt-sign > m){
			while(q[hh]==-1){
				hh++;
				sign--;
			}
			num[q[hh]]--;
			hh++;
		}
	}
	cout << ans;
}

但是我们可以发现打上标记的步骤是一个循环操作,当m很大的时候,这样的算法时间复杂度接近O(n^2),对于题目所给数据会超时
SO 我们要考虑进一步优化
其实我们可以用采用一个数组来记录要删除的元素,例如要删的元素x,我们让 数组[x]+=3,当队列已满要移动队列的时候,考虑数组是否为零,就相当于之前的标记操作,且这种标记方法的复杂度为O(1);
AC code:
1.模拟队列

#include<iostream>
using namespace std;
const int N = 1000010;
int tt,hh,q[N],num[N],ans,lop[N],sign;
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++){
		int x;
		scanf("%d",&x);
		num[x]++;
		q[tt++]=x;		
		if(num[x]==3){
			ans++;
			num[x]-=3;
			lop[x]+=3;
			sign+=3;
		}		
		if(tt-hh-NUM > m){
			while(lop[q[hh]]>0){
				lop[q[hh]]--;
                hh++;
				sign--;
			}
			num[q[hh]]--;
			hh++;	
		}		
	}
	printf("%d",ans);
}

2.STL:

#include<iostream>
#include<queue>
using namespace std;
const int N = 10001000;
int num[N],ans,lop[N],sign;
queue<int> q;
int main(){
	int n,m;
	cin >> n >> m;
	for(int i=0;i<n;i++){
		int x;
		cin >> x;
		num[x]++;
		q.push(x);
		if(num[x]==3){
			ans++;
			num[x]-=3;
			lop[x]+=3;
			sign+=3;
		}
		if(q.size()-sign > m){
			while(lop[q.front()]>0){
				lop[q.front()]--;
                q.pop();
				sign--;
			}
			num[q.front()]--;
			q.pop();
		}
	}
	cout << ans;
}

扩展

将本题的 ai(1<=ai<=1e6)改成 ai (1<=ai<=1e9
由于数组的大小开不到 1e9,所以采用以上方法不能解决
思路:可以先采用 map 或 离散化 预处理:
code:

#include<iostream>
#include<queue>
#include<map> 
using namespace std;
const int N = 10001000;
int ans,sign;
map<int,int> num,lop;
queue<int> q;
int main(){
	int n,m;
	cin >> n >> m;
	for(int i=0;i<n;i++){
		int x;
		cin >> x;
		num[x]++;
		q.push(x);
		if(num[x]==3){
			ans++;
			num[x]-=3;
			lop[x]+=3;
			sign+=3;
		}
		if(q.size()-sign > m){
			while(lop[q.front()]>0){
				lop[q.front()]--;
                q.pop();
				sign--;
			}
			num[q.front()]--;
			q.pop();
		}
	}
	cout << ans;
}

在这里插入图片描述ps. m和n也要改小一点 eg. 3*1e5 应该就ok

### 关于DS队列中组队列的概念与实现 #### 组队列的基本概念 组队列是一种特殊的队列结构,其中的元素按照一定的逻辑被划分为多个小组。这些小组内部的数据可能具有某种关联性或共同属性[^1]。在实际应用场景中,组队列能够有效地管理和调度不同类型的资源或任务。 #### 组队列的核心操作 组队列支持两种基本命令:入队(enqueue)和出队(dequeue)。这两种操作遵循队列的一般特性,“先进先出”原则仍然适用。然而,在组队列中,入队和出队可能会涉及特定的分组处理逻辑。例如,某些实现允许按组优先级进行出入队操作。 #### 使用数组实现顺序组队列 如果通过C语言中的数组来实现组队列,则需要预先定义一个足够大的数组作为底层存储容器。同时,为了维护队列头部和尾部的位置关系,还需引入两个变量`front`和`rear`分别表示当前队头和队尾索引位置[^2]。对于多组的情况,可以通过额外的元数据记录每组起始结束范围或者使用标志位区分各组边界。 以下是基于数组的一个简单示例代码片段展示如何构建基础版本的顺序组队列: ```c #define MAX_SIZE 100 // 假设最大容量为100 typedef struct { int data[MAX_SIZE]; int front; int rear; } GroupQueue; // 初始化函数 void init(GroupQueue *q) { q->front = -1; q->rear = -1; } // 判断是否为空 int isEmpty(GroupQueue *q){ return (q->front == -1); } // 入队操作 void enqueue(GroupQueue *q, int value){ if(q->rear >= MAX_SIZE-1){ printf("Overflow\n"); return ; } if(isEmpty(q)){ q->front=0; } q->data[++(q->rear)] = value; } ``` #### 链式存储下的组队列设计思路 当考虑动态分配空间时,单链表成为另一种可行的选择方案之一。此时每个节点除了保存自身的值外还应附加指向下一个同属一组成员的链接地址字段;另外设立专门用于标识整个队伍首末端点的辅助结构体实例化对象,便于统一控制访问权限并简化算法流程复杂度[^3]。 综上所述,无论是静态还是动态方法都可以用来完成对组队列的有效编码实践过程,并且它们各自具备独特优势特点值得深入探讨研究下去。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值