题解 P2482 [SDOI2010]猪国杀。超级大模拟题 ,值得一刷!然而它掉紫了 \kk
做题历程:
CODE
如下(长达 220 行):
#include<bits/stdc++.h>
using namespace std;
int n,m,order[10+5],cards[2000+5],cacnt=0,FPcnt=0;
string winner;
class pig //猪类
{
public:
bool Ftag,dead,shot,taged;//类反猪标记,死亡标记,连弩标记,是否表明身份
int hp,cardnum,id,hiter;//体力,手牌数,阵营,上一个对它造成伤害的猪
char card[2000+5];//手牌
pig() //构造函数,初始化
{
hp=4,Ftag=0,dead=0,shot=0,id=0,taged=0,cardnum=0;
memset(card,0,sizeof(card));
}
void hit(int hitpig) //掉体力
{
hp--;
hiter=hitpig;
while(own('P')&&hp<=0)
hp++,del('P');
if(hp<=0)
FPcnt-=(id==2&&dead==0),dead=1;
}
void del(char a) //弃牌
{
int pos;
for(int i=1; i<=cardnum; i++)
if(card[i]==a)
{
pos=i;
break;
}
for(int i=pos; i<=cardnum+1; i++)
card[i]=card[i+1];
cardnum--;
}
int own(char a) //拥有某种牌数
{
int num=0;
for(int i=1; i<=cardnum; i++)
num+=(card[i]==a);
return num;
}
void addcard(int num) //摸牌
{
for(int i=1; i<=num; i++)
++cardnum,++cacnt,card[cardnum]=cards[min(cacnt,m)];
}
}pigs[10+5];
bool checkwin() //是否结束
{
if(pigs[1].dead)
{
winner="FP";
return 0;
}
else if(FPcnt<=0)
{
winner="MP";
return 0;
}
else return 1;
}
int next(int a) //逆时针下一只猪
{
a=(a==n?1:a+1);
while(pigs[a].dead)a=(a==n?1:a+1);
return a;
}
void prize(pig killed) //奖励与惩罚
{
if(killed.dead&&killed.hiter==1&&killed.id==1)
pigs[1].cardnum=0,pigs[1].shot=0;
if(killed.dead&&killed.id==2)
pigs[killed.hiter].addcard(3);
}
bool invulnerable(int k,int i) //无懈
{
bool flag=1;
int conflag=0;//是否被抵消,阈值
for(int p=i; conflag<=n; p=next(p)) //无懈
if(pigs[p].own('J')&&pigs[p].id!=pigs[k].id&&flag==0&&pigs[k].taged)
flag^=1,pigs[p].del('J'),pigs[p].taged=1,conflag=0;
else if(pigs[p].own('J')&&pigs[p].id==pigs[k].id&&flag==1&&pigs[k].taged)
flag^=1,pigs[p].del('J'),pigs[p].taged=1,conflag=0;
else conflag++;
return flag;
}
void work() //主要的游戏函数
{
for(int i=1; checkwin(); i=next(i))
{
bool gun=1;
pigs[i].addcard(2);
for(int j=1; j<=pigs[i].cardnum; j++)
{
if(pigs[i].card[j]=='Z')//猪哥连弩
pigs[i].shot=1,pigs[i].del('Z'),j=0;
if(pigs[i].card[j]=='P'&&pigs[i].hp<4)//桃
pigs[i].hp++,pigs[i].del('P'),j=0;
if((gun||pigs[i].shot)&&pigs[i].card[j]=='K')//杀
if((pigs[next(i)].id!=pigs[i].id&&pigs[next(i)].taged)||(i==1&&pigs[next(i)].Ftag&&(!pigs[next(i)].taged)))
{
int nxt=next(i);
pigs[i].del('K');
if(pigs[nxt].own('D')) pigs[nxt].del('D');
else pigs[nxt].hit(i);
if(!checkwin()) return;
prize(pigs[nxt]);
pigs[i].taged=1,gun=pigs[i].shot,j=0;
}
if(pigs[i].card[j]=='F') //决斗
{
int k;
if(pigs[i].id==2)
k=1;
else
for(k=next(i);;k=next(k))
if(pigs[k].id==2&&pigs[k].taged==1)
break;
else if(i==1&&pigs[i].id==1&&pigs[k].Ftag&&(!pigs[k].taged))
break;
else if(k==i)
break;
if(k!=i)
{
pigs[i].del('F');
j=0;
pigs[i].taged=1;
if(invulnerable(k,i))
{
if(i==1&&pigs[k].id==1)
{
pigs[k].hit(i);
if(!checkwin()) return;
prize(pigs[k]);
}
else
{
while(1)
{
if(pigs[k].own('K')) pigs[k].del('K');
else
{
pigs[k].hit(i);
break;
}
if(pigs[i].own('K')) pigs[i].del('K');
else
{
pigs[i].hit(k);
break;
}
}
if(!checkwin()) return;
if(pigs[i].dead)
pigs[i].cardnum=0,memset(pigs[i].card,0,sizeof(pigs[i].card));
prize(pigs[i]),prize(pigs[k]);
}
}
}
}
if(pigs[i].card[j]=='N'||pigs[i].card[j]=='W') //南猪入侵
{
int k;
char o=(pigs[i].card[j]=='N'?'K':'D');
pigs[i].del(pigs[i].card[j]);
j=0;
for(k=next(i); k!=i; k=next(k))
{
if(invulnerable(k,i))
{
if(pigs[k].own(o))
pigs[k].del(o);
else
{
pigs[k].hit(i);
if(!checkwin()) return;
prize(pigs[k]);
if(k==1)
pigs[i].Ftag=1;
}
}
}
}
}
}
}
int main()
{
string tmp;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>tmp;
if(tmp=="MP")
pigs[i].id=1,pigs[i].taged=1;//所有人一开始都知道主猪的身份
else if(tmp=="FP")
pigs[i].id=2,FPcnt++;
else
pigs[i].id=1;
for(int j=1; j<=4; j++)
cin>>pigs[i].card[++pigs[i].cardnum];
}
for(int i=1;i<=m;i++)
cin>>tmp,cards[i]=tmp[0];
work();
cout<<winner<<endl;
for(int i=1;i<=n;i++)
{
if(pigs[i].dead)
cout<<"DEAD";
else
for(int j=1; j<=pigs[i].cardnum; j++)
cout<<pigs[i].card[j]<<' ';
cout<<endl;
}
return 0;
}
思路
此题的主要难点是:题目细节巨多,模拟过程繁琐!
注意事项
- 牌建议用数组存。
- 反猪是 ,不是 。
- 所有人一开始都知道主猪的身份。
- 注意摸牌要从牌堆中从左向右摸,并一个接一个地放在最右边**。注意:当牌堆没牌时要一直摸最后一张牌**。
- 出牌时要从左至右。
- 注意一旦达成胜利条件,游戏立刻结束,因此即使会摸张牌或者还有牌可以用也不要执行了。因此,一只猪死后,一定要判断一下游戏是否结束。
- 注意距离是单向的,且一个角色的死亡会导致一些猪距离的改变。
- 注意,若你采取用循环遍历每只猪的牌(如,
for(int i=1;i<=pigs[i].cardnum;i++)
),每次出牌后建议将循环变量(例子中的i
)归 。否则会 WA。原因:使用某些牌后,可能使它之前的手牌变得可用,所以使用手牌后,要从头扫描手牌。
对于 10% 数据
- 没有锦囊牌,所以不存在『 类反猪 』,所以主猪不会杀死忠猪。但是要考虑杀死反猪有 张牌奖励。
- 『杀 / 』:
- 只能对下家使用,且下家不是与自己一伙的。否则不出。一定要找清目标!
- 发动时,发起者会明确身份。
- 『桃 / 』:只能对自己用。如果该玩家的体力降到 或者更低,并且自己手中没有足够的桃使得自己的体力值回到 ,那么就死亡了。使用桃有三种情况:
- 在自己回合外,体力小于等于 必须用。
- 在自己回合内,体力小于 必须用。
- 否则不能用。
- 『闪 / 』:一定不是自己回合用的。建议:一只猪出杀后判断被杀的猪有没有闪,有就抵消。
- 装上猪哥连弩后:
- 可以一次杀多猪。
- 注意猪哥连弩牌前面的杀别忽略,可以出了。
- 若再摸到猪哥连弩直接弃掉。
对于 30% 数据
类反猪一定只对主猪而言,且其未跳身份。
注意反猪死亡时,最后一个伤害来源处(即使是反猪)立即摸 张牌。忠猪死亡时,如果最后一个伤害来源是主猪,那么主猪所有装备牌、手牌被弃置,因此别忘了弃置猪哥连弩!
关于决斗:
- 决斗时,进攻发起者可以要使用多张杀,所以要处理好决斗时弃掉杀的数量。
- 决斗不限制距离。
- 决斗时,只有被决斗猪的杀比进攻发起者多,才会赢得决斗。
- 注意,决斗的伤害来源来自赢的那一方,而不是发起者。
- 若主猪对忠猪决斗,忠猪是不会出杀的,因此忠猪会掉 点体力(忠猪直接自残)。但是不算跳忠。
- 在决斗时,发起者会明确身份。
- 在决斗时,若发起者因决斗失败而死,要停止它的回合,不能让已经死亡的猪继续使用手牌。
- 反猪只会决斗主猪。
关于南猪入侵与万箭齐发:
『 没有跳身份,且用南猪入侵 / 万箭齐发对主猪造成伤害的猪 』是类反猪。 『 没伤害到不算 』指主猪弃掉了相应的杀 / 闪。
发动时,发起者不会明确身份。
注意,有南猪入侵、万箭齐发,必然使用。
可能会有多只猪会死亡,会摸多张牌。
主猪发动时可能会导致忠猪、反猪死亡。注意惩罚、奖励的顺序。
对于 100% 数据
题目所述确实有点绕,比如:
献殷勤:使用无懈可击挡下南猪入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;
表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤。
两个行为的描述的后一分句表示使用无懈抵消锦囊牌的作用(注意无懈本身也是锦囊牌,这就形成了递归)。
无懈可击可对任意猪使用(包括自己)。
发动无懈可击时,发起者应当是已经暴露身份的或者会暴露身份。
无懈可击只能对明确身份的猪发动。
『 每次有 张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会 』指,一只猪发动锦囊牌,就会从自己开始(包括自己,如忠猪发动锦囊牌时会伤害主猪,忠猪会自行无懈,抵消对主猪的作用。我 无 懈 我 自 己),依次询问是否使用无懈可击(前提是自己有)。
锦囊即将对一只猪生效时,和同一派的,一定想方设法使锦囊牌无效;不一派的,一定想方设法使初始锦囊牌生效(因为在这个题目中,锦囊牌都是负面效果)。即:
- 每只猪都会无条件帮队友无懈掉锦囊牌。
- 每只猪都会无条件无懈掉对手的无懈。
- 每只猪都不会无懈掉队友的无懈。
再强调一遍,无懈询问是从使用这张锦囊的猪开始的,而不是从即将生效的猪开始。
无懈可击只能用于锦囊牌生效前。所以无懈可击优先于锦囊牌生效。
发动决斗时,已经有猪出杀(即在决斗过程中)时不能无懈可击。
看到大佬们的无懈可击都是递归实现的。其实用 for 循环就够了!如果这样是错误的,欢迎评论!这就叫,
人理解迭代,猪理解递归。bool flag=1;int conflag=0; for(int p=i;conflag<=n;p=next(p))//无懈 if(pigs[p].own('J')&&pigs[p].id!=pigs[k].id&&flag==0&&pigs[k].taged) flag^=1,pigs[p].del('J'),pigs[p].taged=1,conflag=0; else if(pigs[p].own('J')&&pigs[p].id==pigs[k].id&&flag==1&&pigs[k].taged) flag^=1,pigs[p].del('J'),pigs[p].taged=1,conflag=0; else conflag++; if(flag) //do something
吐槽
在决斗时,有时发起者竟决斗失败,死亡了。这是为什么?
每当主猪即将受到锦囊伤害时,忠猪和反猪两阵营一定有一方,一张无懈可击都没有了。这是为什么?
主猪对忠猪决斗,忠猪不会出杀,忠猪掉了 点体力。忠猪直接自残了,还不『 忠 』。这是为什么?
当牌堆没牌时要一直摸最后一张牌。这是为什么?
出牌时要从左至右,而不能自由出牌。这是为什么?
这是因为,这是猪🐖国杀!果然是 Pig 思维!
加油,你会 AC 的!
本文作者:Yurchiu
本文链接:https://yz-hs.github.io/57ec00d44f00/
版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。