算法与数据结构实验题 4.19 优质的随从慕名而来

实验任务

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

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

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

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

数据输入

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

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

数据输出

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

输入示例

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

输出示例

2

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=2e9+1;

int n,m,x,num[1000010],que[1000010],ans,cnt;
queue<int>q;

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) {
		scanf("%d",&x);
		num[x]++;
		q.push(x);
		if(num[x]==3) {
			ans++;
			num[x]-=3;
			que[x]+=3;
			cnt+=3;
		}
		if(q.size()-cnt>m) {
			while(que[q.front()]>0){
				que[q.front()]--;
				q.pop();
				cnt--;
			}
			num[q.front()]--;
			q.pop();
		}
	}
	printf("%d",ans);
	return 0;
}

思路
输入数据的数量n,队列大小为m+1,之后为n个数据,我们用a1,a2……ai来表示,将这些数字依次放入队列,若队列中出现三个相同的数字时,ans++,将三个相同数字弹出队列

若直接考虑模拟的话,在已有队列中找到一个数的时间复杂度为O(n)效率堪忧,若每次都去找到后删除,必然会超时,因此考虑其它算法

第一个要解决的问题是:记录每个数字的出现次数,那么用一个数组记录即可,若数据后期增大到1e9,则可以通过unordered_map的哈希表形式进行储存,每一次循环读入代码如下

scanf("%d",&x);
num[x]++;	//x的数量加一
q.push(x);	//采用stl中的queue

第二个要解决的问题是,怎么样高效地实现“删除三个相同数据”

首先,我们需要删除操作时,当且仅当队列已满;既然我们在删除时都需要将n个数据出队一遍,那么不妨考虑在出队时,对“三连”的数字进行处理,即在入队时进行标记操作,在最后一并处理,可以将程序的时间复杂度降至整体O(n)

if(num[x]==3) {
ans++;		//三连时答案加一
	num[x]-=3;		//将三个人清出队列(数量减三)
	que[x]+=3;		//懒标记,在出队操作时需要一并			  移除的数量
	cnt+=3;		//队列大小暂时扩充3,出队时收缩
}

因此,出队时的代码也就很明确了

if(q.size()>=m+1+cnt) {	//当队列大小大于原有大小加扩充值
while(que[q.front()]>0) {	//当碰到打上标记的数据时
	que[q.front()]--;	//标记数量减一
	q.pop();		//出队标记元素
	cnt--;		//队列扩充大小减一
}
num[q.front()]--;		//将本来需要出队的数据出队
q.pop();		//数据数量减一
}

本题考验了我们对队列这一数据结构的时间复杂度熟练度,以及“用空间换时间”的实践思考,通过“打标记”这一操作,可以成功地把时间复杂度降至O(n)

posted @ 2024-11-25 22:46  Severj  阅读(178)  评论(0)    收藏  举报