洛谷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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章