Yurchiu's Blog

P2482 [SDOI2010] 猪国杀

Yurchiu 2020-03-11, 08:53:30 2.9k 隐藏左右两栏 展示左右两栏

题解 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;
}

思路

此题的主要难点是:题目细节巨多,模拟过程繁琐!

注意事项

  • 牌建议用数组存。
  • 反猪是 FP\texttt{FP},不是 AP\texttt{AP}
  • 所有人一开始都知道主猪的身份。
  • 注意摸牌要从牌堆中从左向右摸,并一个接一个地放在最右边**。注意:当牌堆没牌时要一直摸最后一张牌**。
  • 出牌时要从左至右
  • 注意一旦达成胜利条件,游戏立刻结束,因此即使会摸33张牌或者还有牌可以用也不要执行了。因此,一只猪死后,一定要判断一下游戏是否结束。
  • 注意距离是单向的,且一个角色的死亡会导致一些猪距离的改变。
  • 注意,若你采取用循环遍历每只猪的牌(如,for(int i=1;i<=pigs[i].cardnum;i++)),每次出牌后建议将循环变量(例子中的i)归 00。否则会 WA。原因:使用某些牌后,可能使它之前的手牌变得可用,所以使用手牌后,要从头扫描手牌。

对于 10% 数据

  • 没有锦囊牌,所以不存在『 类反猪 』,所以主猪不会杀死忠猪。但是要考虑杀死反猪有 33​ 张牌奖励。
  • 『杀 / K\texttt{K}』:
    • 只能对下家使用,且下家不是与自己一伙的。否则不出。一定要找清目标!
    • 发动时,发起者会明确身份。
  • 『桃 / P\texttt{P}​​』:只能对自己用。如果该玩家的体力降到 00​​ 或者更低,并且自己手中没有足够的桃使得自己的体力值回到 11​​,那么就死亡了。使用桃有三种情况:
    • 在自己回合外,体力小于等于 00​ 必须用。
    • 在自己回合内,体力小于 44 ​必须用。
    • 否则不能用。
  • 『闪 / D\texttt{D}:一定不是自己回合用的。建议:一只猪出杀后判断被杀的猪有没有闪,有就抵消。
  • 装上猪哥连弩后:
    • 可以一次杀多猪
    • 注意猪哥连弩牌前面的杀别忽略,可以出了。
    • 若再摸到猪哥连弩直接弃掉。

对于 30% 数据

  • 类反猪一定只对主猪而言,且其未跳身份。

  • 注意反猪死亡时,最后一个伤害来源处(即使是反猪立即33​ 张牌。忠猪死亡时,如果最后一个伤害来源是主猪,那么主猪所有装备牌手牌被弃置,因此别忘了弃置猪哥连弩!

  • 关于决斗:

    • 决斗时,进攻发起者可以要使用多张杀,所以要处理好决斗时弃掉杀的数量。
    • 决斗不限制距离。
    • 决斗时,只有被决斗猪的杀比进攻发起者多,才会赢得决斗。
    • 注意,决斗的伤害来源来自赢的那一方,而不是发起者。
    • 若主猪对忠猪决斗,忠猪是不会出杀的,因此忠猪会掉 11​ 点体力(忠猪直接自残)。但是不算跳忠。
    • 在决斗时,发起者会明确身份。
    • 在决斗时,若发起者因决斗失败而死,要停止它的回合,不能让已经死亡的猪继续使用手牌。
    • 反猪只会决斗主猪。
  • 关于南猪入侵与万箭齐发:

    • 『 没有跳身份,且用南猪入侵 / 万箭齐发对主猪造成伤害的猪 』是反猪。 『 没伤害到不算 』指主猪弃掉了相应的杀 / 闪。

    • 发动时,发起者不会明确身份。

    • 注意,有南猪入侵、万箭齐发,必然使用。

    • 可能会有多只猪会死亡,会摸多张牌。

    • 主猪发动时可能会导致忠猪、反猪死亡。注意惩罚、奖励的顺序。

对于 100% 数据

  • 题目所述确实有点绕,比如:

    献殷勤:使用无懈可击挡下南猪入侵、万箭齐发、决斗;使用无懈可击抵消表敌意;

    表敌意:对某个角色使用杀、决斗;使用无懈可击抵消献殷勤。

    两个行为的描述的后一分句表示使用无懈抵消锦囊牌的作用(注意无懈本身也是锦囊牌,这就形成了递归)。

  • 无懈可击可对任意猪使用(包括自己)。

  • 发动无懈可击时,发起者应当是已经暴露身份的或者会暴露身份。

  • 无懈可击只能对明确身份的猪发动。

  • 『 每次有 11​​ 张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会 』指,一只猪发动锦囊牌,就会从自己开始(包括自己,如忠猪发动锦囊牌时会伤害主猪,忠猪会自行无懈,抵消对主猪的作用。我 无 懈 我 自 己),依次询问是否使用无懈可击(前提是自己有)。

  • 锦囊即将对一只猪生效时,和同一派的,一定想方设法使锦囊牌无效;不一派的,一定想方设法使初始锦囊牌生效(因为在这个题目中,锦囊牌都是负面效果)。即:

    • 每只猪都会无条件帮队友无懈掉锦囊牌。
    • 每只猪都会无条件无懈掉对手的无懈。
    • 每只猪都不会无懈掉队友的无懈。
  • 再强调一遍,无懈询问是从使用这张锦囊的猪开始的,而不是从即将生效的猪开始。

  • 无懈可击只能用于锦囊牌生效前。所以无懈可击优先于锦囊牌生效

  • 发动决斗时,已经有猪出杀(即在决斗过程中)时不能无懈可击。

  • 看到大佬们的无懈可击都是递归实现的。其实用 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
    

吐槽

  • 在决斗时,有时发起者竟决斗失败,死亡了。这是为什么?

  • 每当主猪即将受到锦囊伤害时,忠猪和反猪两阵营一定有一方,一张无懈可击都没有了。这是为什么?

  • 主猪对忠猪决斗,忠猪不会出杀,忠猪掉了 11 ​点体力。忠猪直接自残了,还不『 忠 』。这是为什么?

  • 当牌堆没牌时要一直摸最后一张牌。这是为什么?

  • 出牌时要从左至右,而不能自由出牌。这是为什么?

这是因为,这是🐖国杀!果然是 Pig 思维!

加油,你会 AC 的!





本文作者:Yurchiu

本文链接:https://yz-hs.github.io/57ec00d44f00/

版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。


By Yurchiu.
其他物件杂物收纳
Hitokoto

Yurchiu 说,除了她以外的人都很强!嘤嘤嘤~~
博客信息
文章数目
158
最近更新
08-21
本站字数
350.6k
文章目录