Luogu P2482 [SDOI2010]猪国杀

2023-05-04,,

这道题在模拟界地位不亚于Luogu P4604 [WC2017]挑战在卡常界的地位了吧。

早上到机房开始写,中间因为有模拟赛一直到1点过才正式开始码。

一边膜拜CXR dalao一边写到3点左右,然后调啊调

最后发现杀死反猪抽的牌并没有被杀死它的人抽走(大雾),4点左右终于是写完了

看完题目(看都要10min)感觉这肯定不是什么建图跑XX算法或者套一个数据结构的题

然而数据范围也很默契,猪和牌的数量都不多,所以我们祭出被我们遗弃的暴力算法——模拟。

题目意思都不多说了,以下讲一下大致的思路:

    每个猪应该有两个身份要记录。一个是真实身份,另一个是当前在游戏中目前的身份,即有可能是未知和类反猪
    关于猪本身一些手牌,血量,有没有武器,下一头猪(因为可能有猪会死掉)等要记录的就不多说了
    每个回合里分别便利当前猪的所有手牌并判断是否可以打出(没有目标自然是不能出的)
    判断每个行动以及更新在此过程中可能会被更新的身份信息
    无懈可击的情况要特别判断,写一个递归即可(实现看代码)

大致思路就是这样了,为了造福人类还是说一下这道题的坑点:

    题目中描述反猪为AP,实际上是FP(看样例就知道了)
    题目里说“数据保证牌的数量够用。”就是在扯淡。如果没牌要一直摸最后一张牌
    MP干掉ZP后武器也得弃了
    无懈去无懈别人的无懈时要注意和原来的行为性质刚好相反(因为只有敌人会无懈你的无懈
    一头猪在自己回合也会GG(决斗把自己玩死),同时在自己回合也可能摸牌(FP和你决斗然后它GG了)
    除了桃之外的牌其他的牌在打出后都得重新扫描手牌(因为可能因为有武器使得之前的杀也可以用了,或者是由于一些猪身份的公开使得之前的一些牌有目标了)
    只有MP会攻击类反猪,ZP是不会对类反猪表敌意的
    放AOE技能时可能会一次干掉很多人,就会产生比如一回合摸许多牌的情况
    同上,MP用AOE技能时要注意结算的顺序,尤其是杀了ZP又杀死FP会导致手上还有牌,而先杀死FP再杀ZP就一张牌都没有了
    MP所有FP死了游戏立即结束,意味着有牌摸的也不用摸了

大致就是这些了,还有强烈建议写的时候打点注释上去省的到时候自己都不知道自己在写什么心态爆炸

贴一下CODE(注释都是写的时候打上去的),如果要看超级详细的解析CODE的移步CXR dalao's blog

CODE

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#define RI register int
#define GC(x) P[x].hand_cards[++P[x].tot]=getcard(); //x摸牌,太长了所以用下define
using namespace std;
const int M=2005;
class FileInputOutput
{
private:
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define S 1<<21
char Fin[S],Fout[S],*A,*B; int Ftop;
public:
FileInputOutput() { A=B=Fin; }
inline void pc(char ch)
{
Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch);
}
inline void gc(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void ps(const char *s)
{
int len=strlen(s); for (RI i=0;i<len;++i) pc(s[i]);
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef tc
#undef S
}File;
struct Pig
{
int type,status,tot,hp,nxt; char hand_cards[M]; //hand_cards手牌,若为'U'则表示这张牌出掉了
//type表示真实身份(1:MP;2:ZP;3:FP),status表示表露出的身份(0:未知;1:MP;2:ZP;3:FP:4:类反)
bool isZ,isalive; //isZ表示是否装备猪哥连弩,isalive表示是否存活
}P[12]; int n,m,cnt,now; char cards[M],ch;
inline char getcard(void)
{
if (cnt<m) return cards[++cnt]; else return cards[cnt];
}
inline int getnext(int x) //找下一头猪,可以路径压缩
{
return P[P[x].nxt].isalive?P[x].nxt:P[x].nxt=getnext(P[x].nxt);
}
inline int find(int x,char y) //查找x是否有牌y,有返回位置,否则返回-1
{
for (RI i=1;i<=P[x].tot;++i) if (P[x].hand_cards[i]==y) return i; return -1;
}
inline void Endgame(void) //游戏结束,输出结果
{
if (P[1].isalive) File.ps("MP\n"); else File.ps("FP\n");
for (RI i=1;i<=n;++i)
{
if (!P[i].isalive) { File.ps("DEAD\n"); continue; }
for (RI j=1;j<=P[i].tot;++j) if (P[i].hand_cards[j]!='U')
File.pc(P[i].hand_cards[j]),File.pc(' '); File.pc('\n');
}
File.Fend(); exit(0);
}
inline void Hurt(int x,int y) //x对y造成了一点伤害
{
if (--P[y].hp) return; int pos=find(y,'P');
if (~pos) return (void)(++P[y].hp,P[y].hand_cards[pos]='U');
P[y].tot=-1; P[y].isalive=0; if (P[y].type==1) Endgame();
if (P[y].type==2&&P[x].type==1) P[x].tot=0,P[x].isZ=0;
if (P[y].type!=3) return; bool isFP=0; RI fr=0,nxt=1;
for (;fr!=nxt;nxt=getnext(nxt),fr=1) if (P[nxt].type==3) { isFP=1; break; }
if (!isFP) Endgame(); GC(x); GC(x); GC(x);
}
inline int indentfy(int x,int y) //鉴定x,y的关系(根据跳的情况判断,注意MP要特判
//(1:同伙;2:敌人;3:y身份未知)
{
if (!P[y].status) return 3; switch (P[x].type)
{
case 1:
if (P[y].status==1||P[y].status==2) return 1;
if (P[y].status==3||P[y].status==4) return 2;
break;
case 2:
if (P[y].status==1||P[y].status==2) return 1;
if (P[y].status==3) return 2; if (P[y].status==4) return 3;
break;
case 3:
if (P[y].status==1||P[y].status==2) return 2;
if (P[y].status==3) return 1; if (P[y].status==4) return 3;
}
return 3;
}
inline void Make_Friendly(int x,int y) //x向y献殷勤,要更新关系
{
if ((P[y].status==1||P[y].status==2)&&P[x].type!=1) P[x].status=2;
if (P[y].status==3) P[x].status=3;
}
inline void Make_Antily(int x,int y) //x向y表敌意,也要更新关系
{
if (P[y].status==1||P[y].status==2) P[x].status=3;
if (P[y].status==3&&P[x].type!=1) P[x].status=2;
}
inline void K(int x,int y) //x向y打出一张杀
{
Make_Antily(x,y); int pos=find(y,'D');
if (~pos) P[y].hand_cards[pos]='U'; else Hurt(x,y);
}
inline bool J(int x,int y,int opt) //依次决定对于x对y打出的锦囊是否有人响应无懈
//opt表示这次无懈行为是献殷勤(1)还是表敌意(2)
{
for (RI nxt=x,fr=0;nxt!=fr;nxt=getnext(nxt),fr=x)
{
if (indentfy(nxt,y)!=opt) continue;
int pos=find(nxt,'J'); if (~pos)
{
P[nxt].hand_cards[pos]='U';
if (opt==1) Make_Friendly(nxt,y); else Make_Antily(nxt,y);
return J(nxt,y,3-opt)^1;
}
}
return 0;
}
inline void F(int x,int y) //x向y进行决斗
{
Make_Antily(x,y); if (J(x,y,1)) return;
if (P[x].type==1&&P[y].type==2) return Hurt(x,y);
int pos; for (;;)
{
pos=find(y,'K'); if (~pos) P[y].hand_cards[pos]='U'; else return Hurt(x,y);
pos=find(x,'K'); if (~pos) P[x].hand_cards[pos]='U'; else return Hurt(y,x);
}
}
inline void N(int x) //x打出南猪入侵
{
for (int nxt=getnext(x);nxt!=x;nxt=getnext(nxt))
{
if (J(x,nxt,1)) continue; int pos=find(nxt,'K');
if (~pos) { P[nxt].hand_cards[pos]='U'; continue; }
if (P[nxt].type==1&&!P[x].status) P[x].status=4; Hurt(x,nxt);
}
}
inline void W(int x) //x打出万箭齐发
{
for (int nxt=getnext(x);nxt!=x;nxt=getnext(nxt))
{
if (J(x,nxt,1)) continue; int pos=find(nxt,'D');
if (~pos) { P[nxt].hand_cards[pos]='U'; continue; }
if (P[nxt].type==1&&!P[x].status) P[x].status=4; Hurt(x,nxt);
}
}
inline void Play(void) //一回合
{
GC(now); GC(now); bool iskilled=0; int nxt;
for (RI i=1;i<=P[now].tot;++i)
{
char &opt=P[now].hand_cards[i]; switch (opt)
{
case 'K':
if (iskilled&&!P[now].isZ) break;
if (indentfy(now,nxt=getnext(now))==2) { opt='U'; iskilled=1; K(now,nxt); i=0; break; } //是敌对关系才能杀
break;
case 'F':
if (P[now].type==3) { opt='U'; F(now,1); i=0; break; } //FP优先杀MP
for (nxt=getnext(now);nxt!=now;nxt=getnext(nxt))
if (indentfy(now,nxt)==2) { opt='U'; F(now,nxt); i=0; break; } //按顺序找人决斗
break;
case 'N':
opt='U'; N(now); i=0; break;
case 'W':
opt='U'; W(now); i=0; break;
case 'P':
if (P[now].hp<4) opt='U',++P[now].hp; break;
case 'Z':
opt='U'; P[now].isZ=1; i=0; break;
}
}
}
#undef GC
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (File.read(n),File.read(m),i=1;i<=n;++i)
{
File.gc(ch); P[i].type=ch=='M'?1:(ch=='Z'?2:3); if (P[i].type==1) P[i].status=1;
P[i].tot=4; for (File.gc(ch),j=1;j<=4;++j) File.gc(P[i].hand_cards[j]);
if(i^n) P[i].nxt=i+1; else P[i].nxt=1; P[i].isalive=1; P[i].hp=4;
}
for (i=1;i<=m;++i) File.gc(cards[i]); for (now=1;;now=getnext(now)) Play(); return 0;
}

Luogu P2482 [SDOI2010]猪国杀的相关教程结束。

《Luogu P2482 [SDOI2010]猪国杀.doc》

下载本文的Word格式文档,以方便收藏与打印。