优质的随从慕名而来
题目
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