题目链接
题意就不描述了,自己看吧。
题解:
有很多地方的搞法和注意事项都写在代码里了,都是模拟,没有什么算法上值得说的。注意好好读题,然后考虑一定要全面,不要漏掉什么东西,或者自以为是,有些地方和你实际打三国杀是不一样的。
这个题估计是我高中生涯的最后一道OI题了,拿它做最后一题是有特殊意义的。我记得曾经机房里有人写这个题的时候,zyb推荐我也去写这些大模拟题。我当时告诉zyb说,等着我退役后有空的时候再去写,现在我退役了,趁着还有机会,就把这个题写了,算是完成了之前说过的话吧。
另一重意义是,很多小伙伴都在NOIP之后退役了。我记得NOIP前后,有一些小伙伴都去写了这个题。我写这个题是为了致敬所有之前退役的OI选手们,特别是我的好友wddwjlss,他退役前帮助了我很多,我对他非常感谢,这道题是他退役后写的,也是他OI生涯的最后一题,我也来写这个题作为我OI生涯的最后一题,来致敬他。
还有一点原因是感觉日后可能这些算法不知道究竟还能用得上多少,但是模拟可能会是最常用的,所以就写一道切合实际的模拟题,来检验一下自己的码力。我记得我学OI的其中一个初衷是想自己去编游戏,学到现在,猪国杀这个题本来就是和三国杀有着很大的联系,起码自己能写出一个简化版的三国杀了,也算是给自己的初衷一个交代吧。
代码10个k左右。
代码:
#include
using namespace std;
int n,m;
int now;//记录当前用到了第几张牌
int gg;//记录本回合是否出过杀,由于杀写成了一个函数,所以定义成一个全局变量
int ed;//记录游戏是否结束
char s[2010];//牌堆
char ss[2010];//一个读入用的数组
struct node
{
int xue;//xue表示当前血量
int num;//num表示当前手牌的数量
int zhu;//zhu记录这只猪是否装备是连弩,0表示没有,1表示有
int die;//die记录这只猪是否还活着,0表示活着,1表示死了
int id;//记录这只猪的身份,1表示主,2表示忠,3表示反
int tiao;//tiao记录这只猪是否跳过身份,0表示没跳,1表示跳了,2表示是类反猪
char s[2010];//记录所有手牌
}a[20];
inline void si(int x,int to)//x使to进入了濒死状态
{
for(int j=1;j<=a[to].num;++j)
{
while(a[to].s[j]=='P')
{
a[to].xue++;
for(int k=j;k<a[to].num;++k)
a[to].s[k]=a[to].s[k+1];
a[to].num--;
if(a[to].xue>0)
break;
}
if(a[to].xue>0)
break;
}
if(a[to].xue==0)//结算奖励和惩罚,并且记录死亡信息
{
a[to].die=1;
int pd=0;//判断这个人死后游戏是否结束
for(int i=1;i<=n;++i)
{
if(a[i].die==0&&a[i].id==3)
{
pd=1;
break;
}
}
if(pd==0||a[1].die)
ed=1;
if(a[to].id==3&&ed==0)//杀死反贼摸三张
{
++a[x].num;
a[x].s[a[x].num]=s[now];
++now;
now=min(now,m);
++a[x].num;
a[x].s[a[x].num]=s[now];
++now;
now=min(now,m);
++a[x].num;
a[x].s[a[x].num]=s[now];
++now;
now=min(now,m);
}
if(a[to].id==2&&a[x].id==1&&ed==0)//主杀了忠,弃掉所有手牌和装备
{
for(int i=1;i<=a[1].num;++i)
a[1].s[i]=0;
a[x].num=0;
a[x].zhu=0;
}
}
}
//杀的返回值是本次是否用了杀
inline int sha(int x,int i)//x为出杀的是哪一方
{
int shu=0;
int to=x+1;//记录距离为1的目标是哪一个
while(1)
{
if(to>n)
to-=n;
if(!a[to].die)
break;
++to;
}
int pd=0;//判断是否应该对目标出杀
if(a[x].id==3&&(a[to].id==1||(a[to].id==2&&a[to].tiao==1)))
pd=1;
if(a[x].id==2&&a[to].id==3&&a[to].tiao==1)
pd=1;
if(a[x].id==1&&((a[to].id==3&&a[to].tiao==1)||a[to].tiao==2))
pd=1;
if(a[x].s[i]=='K'&&(gg==0||a[x].zhu)&&ed==0&&pd==1)
{
++shu;
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
a[x].num--;
gg=1;
pd=0;//记录是否打出了闪
a[x].tiao=1;
for(int j=1;j<=a[to].num;++j)
{
if(a[to].s[j]=='D')
{
pd=1;
for(int k=j;k<a[to].num;++k)
a[to].s[k]=a[to].s[k+1];
a[to].num--;
break;
}
}
if(pd==0)
{
a[to].xue--;
if(a[to].xue==0)
si(x,to);
}
}
return shu;
}
//判断是否要无懈将要对x生效的锦囊,现在从cur开始询问
//opt表示当前是否会对x生效,0表示不会生效,1表示会生效
//函数返回值0表示锦囊没有被无懈,返回值1表示被无懈了
inline int wuxie(int x,int cur,int opt)
{
if(a[x].tiao!=1)
return 0;
int ci=0;
while(1)
{
if(cur>n)
cur-=n;
if(x==cur)
++ci;
if(ci>1)
break;
if(a[cur].die)
{
++cur;
continue;
}
int pd=0;
//判断当前这个人在这种锦囊当前的生效状态下是否应该打出无懈
if(a[cur].id==1&&a[x].id==2&&a[x].tiao==1&&opt==1)
pd=1;
if(a[cur].id==1&&a[x].id==1&&a[x].tiao==1&&opt==1)
pd=1;
if(a[cur].id==1&&a[x].id==3&&a[x].tiao==1&&opt==0)
pd=1;
if(a[cur].id==2&&a[x].id==1&&a[x].tiao==1&&opt==1)
pd=1;
if(a[cur].id==2&&a[x].id==2&&a[x].tiao==1&&opt==1)
pd=1;
if(a[cur].id==2&&a[x].id==3&&a[x].tiao==1&&opt==0)
pd=1;
if(a[cur].id==3&&a[x].id==1&&a[x].tiao==1&&opt==0)
pd=1;
if(a[cur].id==3&&a[x].id==2&&a[x].tiao==1&&opt==0)
pd=1;
if(a[cur].id==3&&a[x].id==3&&a[x].tiao==1&&opt==1)
pd=1;
if(pd==0)
{
++cur;
continue;
}
pd=0;//记录这个人是否真的能打出无懈
for(int i=1;i<=a[cur].num;++i)
{
if(a[cur].s[i]=='J')
{
for(int j=i;j<a[cur].num;++j)
a[cur].s[j]=a[cur].s[j+1];
--a[cur].num;
a[cur].tiao=1;
return wuxie(x,cur,opt^1);
pd=1;
break;
}
}
++cur;
if(pd==1)//这一轮有人打出过无懈就进入下一轮了,不用继续循环了
break;
}
return opt^1;//注意返回值和opt是反着的
}
inline void play(int x)//轮到x的回合了
{
//回合开始的时候摸两张牌
++a[x].num;
a[x].s[a[x].num]=s[now];
++now;
now=min(now,m);
++a[x].num;
a[x].s[a[x].num]=s[now];
++now;
now=min(now,m);
gg=0;//记录本回合是否打出过杀
//每次打出一张牌都要重新考虑可以打出的第一张
//因为万箭和南蛮会让人无懈跳身份,让之前没有出的杀和决斗可以出了
while(1)
{
int pan=0;//记录这一轮有没有打出过牌
for(int i=1;i<=a[x].num;++i)
{
if(ed)//回合进行到一半游戏就结束了
break;
if(a[x].die)//回合中途决斗把自己搞死了
break;
if(pan)
break;
if(a[x].s[i]=='P'&&a[x].xue<4)//桃
{
a[x].xue++;
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
a[x].num--;
pan=1;
break;
}
if(a[x].s[i]=='K'&&ed==0)
pan=sha(x,i);
if(pan)
break;
if(a[x].s[i]=='Z'&&ed==0)
{
a[x].zhu=1;
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
a[x].num--;
pan=1;
break;
}
if(a[x].s[i]=='N'&&ed==0)//南蛮入侵
{
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
a[x].num--;
int cur=x+1;//当前该出杀的是谁
while(1)
{
if(cur>n)
cur-=n;
if(a[cur].die)
{
++cur;
continue;
}
if(cur==x)
break;
int opt=wuxie(cur,x,1);//先判断是否会被无懈
if(opt==0)
{
int pd=0;//记录是否打出了杀
for(int j=1;j<=a[cur].num;++j)
{
if(a[cur].s[j]=='K')
{
pd=1;
for(int k=j;k<a[cur].num;++k)
a[cur].s[k]=a[cur].s[k+1];
--a[cur].num;
break;
}
}
if(pd==0)
{
a[cur].xue--;
if(cur==1)
{
if(!a[x].tiao)
a[x].tiao=2;
}
if(a[cur].xue==0)
si(x,cur);
}
}
if(ed==1)//可能不用全部询问完就游戏结束了
break;
++cur;
}
pan=1;
break;
}
if(a[x].s[i]=='W'&&ed==0)//万箭齐发
{
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
a[x].num--;
int cur=x+1;//当前该出杀的是谁
while(1)
{
if(cur>n)
cur-=n;
if(a[cur].die)
{
++cur;
continue;
}
if(cur==x)
break;
int opt=wuxie(cur,x,1);//先判断是否会被无懈
if(opt==0)
{
int pd=0;//记录是否打出了闪
for(int j=1;j<=a[cur].num;++j)
{
if(a[cur].s[j]=='D')
{
pd=1;
for(int k=j;k<a[cur].num;++k)
a[cur].s[k]=a[cur].s[k+1];
--a[cur].num;
break;
}
}
if(pd==0)
{
a[cur].xue--;
if(cur==1)
{
if(!a[x].tiao)
a[x].tiao=2;
}
if(a[cur].xue==0)
si(x,cur);
}
}
if(ed==1)//可能不用全部询问完就游戏结束了
break;
++cur;
}
pan=1;
break;
}
if(a[x].s[i]=='F'&&ed==0)
{
int cur=x+1;
while(1)
{
if(cur>n)
cur-=n;
if(cur==x)
break;
if(a[cur].die)
{
++cur;
continue;
}
if(a[x].id==1&&((a[cur].id==3&&a[cur].tiao==1)||a[cur].tiao==2))
break;
if(a[x].id==2&&a[cur].id==3&&a[cur].tiao==1)
break;
if(a[x].id==3&&(a[cur].id==1||(a[cur].id==2&&a[cur].tiao==1)))
break;
++cur;
}
if(cur==x)
continue;
if(a[x].id==3)
cur=1;
//打出决斗
for(int j=i;j<a[x].num;++j)
a[x].s[j]=a[x].s[j+1];
--a[x].num;
a[x].tiao=1;
int opt=wuxie(cur,x,1);//先从打出锦囊的人开始询问
if(opt==0)
{
int qwq=1;//记录该哪一方出杀了,0表示出决斗的人,1表示另一个人
if(a[cur].id==2&&a[x].id==1)//特判忠不会在决斗时对主出杀
{
a[cur].xue--;
if(a[cur].xue==0)
si(x,cur);
}
else
{
while(1)
{
int pan=0;//记录当前这一轮的这个人是否打出了杀
int fz=0;//一个辅助变量
if(qwq==1)
fz=cur;
else
fz=x;
for(int j=1;j<=a[fz].num;++j)
{
if(a[fz].s[j]=='K')
{
pan=1;
for(int k=j;k<a[fz].num;++k)
a[fz].s[k]=a[fz].s[k+1];
--a[fz].num;
break;
}
}
if(pan==0)
{
a[fz].xue--;
if(a[fz].xue==0)
{
if(qwq==1)
si(x,cur);
else
si(cur,x);
}
break;
}
qwq^=1;
}
}
}
pan=1;
break;
}
}
if(pan==0)
break;
}
}
inline void huihe()
{
for(int i=1;i<=n;++i)
{
if(a[i].die)
continue;
int pd=0;//判断是否游戏结束
for(int j=1;j<=n;++j)
{
if(a[j].id==3&&a[j].die==0)
pd=1;
}
if(pd==1&&a[1].die==0)
play(i);//进行i的回合
else
break;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%s",ss+1);
if(ss[1]=='M')
a[i].id=1;
if(ss[1]=='Z')
a[i].id=2;
if(ss[1]=='F')
a[i].id=3;
for(int j=1;j<=4;++j)
{
scanf("%s",ss+1);
a[i].num++;
a[i].s[a[i].num]=ss[1];
}
a[i].xue=4;
a[i].die=0;
a[i].zhu=0;
a[i].tiao=0;
}
for(int i=1;i<=m;++i)
{
scanf("%s",ss+1);
s[i]=ss[1];
}
a[1].tiao=1;
now=1;
while(1)
{
huihe();
int pd=0;//判断游戏是否结束
for(int i=1;i<=n;++i)
{
if(a[i].id==3&&a[i].die==0)
pd=1;
}
if(pd==1&&a[1].die==0)
continue;
else
{
if(a[1].die)
printf("FP\n");
else
printf("MP\n");
for(int i=1;i<=n;++i)
{
if(a[i].die)
printf("DEAD\n");
else
{
for(int j=1;j<=a[i].num;++j)
printf("%c ",a[i].s[j]);
printf("\n");
}
}
break;
}
}
return 0;
}