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

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

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

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

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

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

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

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

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

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

  1. [洛谷P2482][SDOI2010]猪国杀

    题目大意:猪国杀,又一道大模拟题 题解:模拟,对于一个没有玩过三国杀的人来说,一堆细节不知道,写的十分吃力 卡点:无数,不想说什么了,这告诉我要多玩游戏 C++ Code: #include < ...

  2. 洛谷P2482 [SDOI2010]猪国杀——题解

    猪国杀,模拟题的一颗耀眼的明珠,成长大牛.锻炼码力必写题! 模拟题没什么思维难度.只要按部就班地去做就是.模拟简单在这,难也在这.因为题面巨长,条件巨多,忽疏一点都有可能全盘皆输.故推荐考试时碰见了, ...

  3. 并不对劲的bzoj1972:loj2885:p2482[SDOI2010]猪国杀

    题目大意 只能放链接了. 题目中有一点没说:并不是保证牌够用,而是在牌不够用时反复抽最后一张牌. 题解 发现玩家的数量比较少,所以可以不太在意时间够不够用. 考虑三件事:1.基本操作,如摸牌.出牌.玩 ...

  4. Luogu2482 [SDOI2010]猪国杀 ---- 模拟

    Luogu2482 [SDOI2010]猪国杀 题意 ...... https://www.luogu.org/problemnew/show/P2482 总结 首先说一下代码的构思: 首先确定了所有 ...

  5. [BZOJ 1972][Sdoi2010]猪国杀

    1972: [Sdoi2010]猪国杀 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 364  Solved: 204[Submit][Status][D ...

  6. 洛谷 P2482 loj #2885 [SDOI2010]猪国杀 题解【模拟】【贪心】【搜索】

    好玩的模拟题. 以后要经常写模拟题鸭 题目描述 游戏背景 <猪国杀>是一种多猪牌类回合制游戏,一共有\(3\)种角色:主猪,忠猪,反猪.每局游戏主猪有且只有\(1\)只,忠猪和反猪可以有多 ...

  7. BZOJ1972:[SDOI2010]猪国杀

    我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html 题目传送门:https://www.lydsy.com/JudgeOnline/problem ...

  8. 【BZOJ1972】[SDOI2010] 猪国杀(恶心的大模拟)

    点此看题面 大致题意: 让你模拟一个游戏猪国杀的过程. 几大坑点 对于这种模拟题,具体思路就不讲了,就说说有哪些坑点. 题面有锅,反猪是\(FP\). 数据有锅,牌堆中的牌可能不够用,牌堆为空之后需一 ...

  9. Bzoj1972: [Sdoi2010]猪国杀 题解(大模拟+耐心+细心)

    猪国杀 - 可读版本 https://mubu.com/doc/2707815814591da4 题目可真长,读题都要一个小时. 这道题很多人都说不可做,耗时间,代码量大,于是,本着不做死就不会死的精 ...

随机推荐

  1. Tsung 超详细的的tsung性能测试资料

    超详细的的tsung性能测试资料 by:授客 QQ:1033553122 由于篇幅问题,采用链接分享的形式 下载连接:理解Tsung配置文件 下载连接:基准测试方法 下载连接:Tsung XML配置文 ...

  2. IDEA错误:Failed to start end point associated with ProtocolHandler [http-nio-9999] java.net.BindException: Address already in use: bind

    日志显示进程端口已被占用,首先需要的是查询什么进程占用了当前的9999端口. 1.win+R输入cmd进入命令界面: 2.输入命令  netstat -ano|findstr "端口号&qu ...

  3. Android--判断listview上下滑动的方法

    elv_music_res_fragment.setOnScrollListener(new AbsListView.OnScrollListener() { private int mLastFir ...

  4. [20171107]dbms_shared_pool.pin补充.txt

    [20171107]dbms_shared_pool.pin补充.txt --//上午的测试,做一些补充,主要还是一些理解问题. 1.环境:SCOTT@book> @ &r/ver1 P ...

  5. 修改Devexpress DateEdit控件默认的日期格式和日历风格

    最近项目中很多地方使用到DevExpress的DateEdit控件,而且设置都是一致(日期格式.选择日期的日历的风格等).每一次都需要设置太痛苦,因此需要寻找一种高效的解决方式. 一. 设置好一个作为 ...

  6. 始终使用属性(Property),而不是字段(Data Member)

    1.始终使用属性(Property),而不是字段(Data Member) C# 属性已经晋升为一等公民,如果你的类中还有public的字段,Stop.访问属性和字段的方式是一样的,但是属性是用方法( ...

  7. Iptables防火墙(SNAT和DNAT)

     1.SNAT:源地址转换 实现内网访问外网,修改IP地址,使用POSTROUTING 命令:iptables  -t  nat  -A POSTROUTING  -s  192.168.1.10/2 ...

  8. js实现Mac触摸板双指事件(上/下/左/右/放大/缩小)

    前言 这几天在修复一个web问题时,需要捕获Mac触摸板双指事件(上.下.左.右.放大.缩小),但发现并没有现成的轮子,还是要自己造. 例如:jquery.mousewheel.js(添加跨浏览器的鼠 ...

  9. vue_表单控件

    Vue.js中提供 v-model 的指令对表单元素进行双向数据绑定,在修改表单元素值的同时,实例 vm 中对应的属性值也同时更新,反之亦然.本小节介绍主要input元素绑定v-model 后的具体用 ...

  10. 【转】Android 创建AVD各参数详解

    一.Eclipse中图形创建AVD: Device: 即设备,指具体的手机设备型号,可以在window->Android Virtual Device Manager->Device De ...