DS homework-队列

优质的随从慕名而来

题目

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值