題意就不描述了,自己看吧。
題解:
有很多地方的搞法和注意事項都寫在代碼裏了,都是模擬,沒有什麼算法上值得說的。注意好好讀題,然後考慮一定要全面,不要漏掉什麼東西,或者自以爲是,有些地方和你實際打三國殺是不一樣的。
這個題估計是我高中生涯的最後一道OI題了,拿它做最後一題是有特殊意義的。我記得曾經機房裏有人寫這個題的時候,zyb推薦我也去寫這些大模擬題。我當時告訴zyb說,等着我退役後有空的時候再去寫,現在我退役了,趁着還有機會,就把這個題寫了,算是完成了之前說過的話吧。
另一重意義是,很多小夥伴都在NOIP之後退役了。我記得NOIP前後,有一些小夥伴都去寫了這個題。我寫這個題是爲了致敬所有之前退役的OI選手們,特別是我的好友wddwjlss,他退役前幫助了我很多,我對他非常感謝,這道題是他退役後寫的,也是他OI生涯的最後一題,我也來寫這個題作爲我OI生涯的最後一題,來致敬他。
還有一點原因是感覺日後可能這些算法不知道究竟還能用得上多少,但是模擬可能會是最常用的,所以就寫一道切合實際的模擬題,來檢驗一下自己的碼力。我記得我學OI的其中一個初衷是想自己去編遊戲,學到現在,豬國殺這個題本來就是和三國殺有着很大的聯繫,起碼自己能寫出一個簡化版的三國殺了,也算是給自己的初衷一個交代吧。
代碼10個k左右。
代碼:
#include <bits/stdc++.h>
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;
}