洛谷2482 BZOJ1972 SDOI2010 猪国杀 模拟

题目链接

题意就不描述了,自己看吧。

题解:
有很多地方的搞法和注意事项都写在代码里了,都是模拟,没有什么算法上值得说的。注意好好读题,然后考虑一定要全面,不要漏掉什么东西,或者自以为是,有些地方和你实际打三国杀是不一样的。

这个题估计是我高中生涯的最后一道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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值