问题:共有四种牌型: 1普通牌(C),轮到下家出牌; 2禁止牌(S),轮到下下家出牌; 3反转牌(R),轮到上家出牌,顺序颠倒; 4罚抽两张牌(D),下家牌数+2,下下家出牌; 给定出牌类型以及顺序,问最后所有人剩下的牌数。
第一行输入n,m 表示人数和字符串长度,第二行输入a[i],表示初始牌数,第三行字符串表示每回合出的牌型。(2<=n,m<=2*10^5)(1<=a[i]<=2*10^5)
样例:输入:3 6
3 2 3
SRDCCD
输出:3
0
3
题解:观察题目不难发现,这是一道模拟题,思路很快就会有,但关键点是处理这道题的方法,如果用int数组存数据并不断遍历,由于牌数为0的玩家会直接退出,无法成为相应的下家,所以在不移除牌为0的玩家的情况下,时间复杂度最坏会达到O(n*m),赛场上测评机就会爆T了~
所以说正确的思路就是利用循环双向链表存数据,每当出现一个牌数为0的玩家就把他移除链表,如此O(m)的时间复杂度便可以轻松飘过这道题了。
而对于循环双向链表,我们可以利用链表每个节点分别存有前指针,后指针,节点数据的特点,利用三个int数组分别储存对应数据,手动模拟链表结构,并通过改变被移除目标前后玩家的前指针和后指针,来模拟实现移除链表的操作,如此便可以模拟游戏过程并成功打出AC代码啦~
AC代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=200005;
int n,m;
int my_prev[N],my_next[N],a[N],ban[N];
string str;
bool st=true;//控制方向 真为顺时针,假为逆时针
signed main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>str;
//初始化循环双向链表
for(int i=1;i<=n;i++)
{
//初始化后继与前继节点
my_next[i]=(i==n)?1:i+1;
my_prev[i]=(i==1)?n:i-1;
}
int pos=1;//位置索引
for(char c:str)
{
if(c=='C')
a[pos]--;
if(c=='S')
{
a[pos]--;
if(st)
ban[my_next[pos]]++;
else
ban[my_prev[pos]]++;
}
if(c=='R')
{
st=!st;
a[pos]--;
}
if(c=='D')
{
a[pos]--;
if(st)
{
ban[my_next[pos]]++;
a[my_next[pos]]+=2;
}
else
{
ban[my_prev[pos]]++;
a[my_prev[pos]]+=2;
}
}
//牌的效果执行完毕,检查是否出完,若出完则踢出该玩家
if(a[pos]==0)
{
int qian,hou;//获取前后玩家的索引
qian=my_prev[pos];
hou=my_next[pos];
my_next[qian]=hou;
my_prev[hou]=qian;
}
//搜索下一个玩家,若是被禁止则跳过
int nextplayer;
if(st)
{
nextplayer=my_next[pos];
while(ban[nextplayer])
{
ban[nextplayer]--;
nextplayer=my_next[nextplayer];
}
}
else
{
nextplayer=my_prev[pos];
while(ban[nextplayer])
{
ban[nextplayer]--;
nextplayer=my_prev[nextplayer];
}
}
//将下一个玩家更新为新的索引位置
pos=nextplayer;
}
//输出答案
for(int i=1;i<=n;i++)
cout<<a[i]<<endl;
return 0;
}
由于本蒟蒻实力有限,无法补完所有题的题解,故只能选做且一个一个发题解,望谅解。
最后嘞------求点赞关注!! !