【题解】P9518 queue
题面传送门
解决思路
其实用链表也可以维护这样一个队列(当时感觉删人会方便点所以写了链表)。
一个比较常用的技巧是使用 stl::map
,可以方便地将字符串作为下标,写起来会简单很多。
然后看懂题意模拟即可,代码里有非常详细的注释。
AC Code
//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
using namespace std;
int n;
string op,s;
string tail="0"; //队尾
map<string,bool> pl,pd; //分别代表一个人是否正在玩,是否正在排队
map<string,string> fro,nxt; //分别代表链表的前驱与后继
vector<string> lst; //存放上一次玩的人
void add(string s){ //在链表的最末尾加上一个人
pd[s]=1; //将此人设为正在排队
nxt[tail]=s,fro[s]=tail,tail=s; //处理前驱后继,并将队尾设为当前人
}
void del(string s){ //删人
pd[s]=0; //将此人设为不在排队
if(s==tail) tail=fro[s]; //如果此人是队尾,直接用改队尾的方法删
else{ //否则就是常规的链表删点
nxt[fro[s]]=nxt[s];
fro[nxt[s]]=fro[s];
}
}
signed main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++){
cin>>op;
if(op[0]=='a'){ //加人操作
cin>>s;
if(pd[s]) cout<<"Error"<<'\n'; //如果此人已在队中,Error
else add(s),cout<<"OK"<<'\n';
}
else if(op[0]=='l'){ //删人操作
cin>>s;
if(!pd[s]||pl[s]) cout<<"Error"<<'\n'; //如果此人不在队中或正在玩,Error
else del(s),cout<<"OK"<<'\n';
}
else if(op[0]=='s'){ //游玩操作
if(lst.size()){
for(auto i:lst){ //把上一次玩的删掉,并且加入队尾
pl[i]=0;
del(i),add(i);
}
lst.clear();
}
string _1=" ",_2=" ";
if(tail!="0"){ //尝试取第一个人
_1=nxt["0"];
if(tail!=_1) _2=nxt[_1]; //尝试取第二个人
}
if(_1==" ") cout<<"Error"<<'\n'; //没人
else if(_2==" ") cout<<_1<<'\n',lst.pb(_1),pl[_1]=1; //有一个人
else cout<<_1<<' '<<_2<<'\n',lst.pb(_1),lst.pb(_2),pl[_1]=pl[_2]=1; //有两个人
}
}
return 0;
}