P2482 [SDOI2010] 猪国杀

xing_ling 2022-08-23 21:04:20

代码虽长但很详细

每个模拟点都用函数分开了

#include<bits/stdc++.h>
using namespace std;

int seat[30];//由于无懈,南蛮等牌的需求,方便从每一名玩家开始向逆时针遍历所有玩家 
int n,m;
int king;//主公的编号 
int fnum;//反贼的数量 
bool lei[15];//类反贼标记 
int identity[15];//身份 
int predict[15];//别人对他身份的推测(即是否跳了身份,若跳了则是身份,没跳则是0) 
queue<char> card;//摸牌堆 
vector<char> player[15];//每名玩家的手牌 
bool dead[15];//是否死亡 
int hp[15];//血量 
int dis[15];//根据题意,对于每名玩家,只会有一名其他玩家与他距离为1,dis[i]即表示与i距离为1的那位玩家 
bool Klimit;//出杀限制,若当前回合出过杀则为1否则为0,每一个新回合更新为0 
bool equip[15];//是否装备了诸葛连弩 
int gameover;//游戏是否结束,没结束则为0,主公胜利则为1,反贼胜利则为2 

void update_dis()//更新距离 
{
	for(int i=1;i<=n;i++)
	{
		int tt=i+1;
		while(dead[seat[tt]]==1)
		{
			tt++;
		}
		dis[i]=seat[tt];
	}
}

void get_card(int p,int cnt)//让玩家p从牌堆中摸cnt张牌 
{
	for(int i=1;i<=cnt;i++)
	{
		player[p].push_back(card.front());
		if(card.size()!=1)
			card.pop();
	}
}

void dying(int from,int p)//当玩家p进入濒死状态时,伤害源时from,
{
	//判断玩家p是否有桃 
	bool flag=0;
	for(int i=0;i<player[p].size();i++) 
	{
		if(player[p][i]=='P')
		{
			player[p].erase(player[p].begin()+i);
			flag=1;
			break;
		}
	}
	if(flag==1)
		hp[p]++;
	else//没有桃,那就处理后事( 
	{
		dead[p]=1;//玩家p死亡 
		if(p==king)//如果是主公那么游戏结束 
		{
			gameover=2;
			return;
		}
		
		if(identity[p]==2)//如果是反贼 
		{
			fnum--;//反贼数量-1 
			if(fnum==0)//如果反贼都没了那么游戏结束 
			{
				gameover=1;
				return;//!!!如果这里不结束那么会导致伤害源多摸三张牌,输出时伤害源的手牌会多三张牌!!! 
			}
			get_card(from,3);//伤害源摸3张牌 
		}
		
		if(identity[p]==1 && from==king)//如果玩家p是忠臣并且伤害源是主公 
		{
			player[king].clear();//清空手牌 
			equip[king]=0;//!!!装备栏也要清空!!! 
		}
		
		update_dis();//每有玩家死亡就更新距离 
	}
}
//无懈可击 
int J(int from,int to,int k)//玩家from向玩家to打出一张锦囊牌,其中锦囊牌对玩家to的性质是k(k为0表示表敌意,k为1表示献殷勤) 
{ 
	//我们要寻找是否有玩家会为这张锦囊牌打出无懈可击,如果打出无懈可击且生效了返回1,否则返回0 
	//性质:predict[to]不为0(没人会给没亮身份的玩家出无懈可击,包括他自己) 
	for(int i=from;i<n+from;i++)//逆时针遍历所有玩家 
	{
		if(dead[seat[i]])continue;//如果死亡就跳过 
		int x=seat[i]; 
		if(identity[x]==predict[to] && k==0)//如果该玩家与玩家to同阵营而且这张锦囊牌对玩家to产生不好的影响 
		{
			for(int j=0;j<player[x].size();j++)//寻找无懈可击 
			{
				if(player[x][j]=='J')//找到了 
				{
					player[x].erase(player[x].begin()+j);
					predict[x]=identity[x];//玩家x跳身份了 
					if(J(x,to,1))//无懈可击也是锦囊牌,判断是否有玩家y无懈这张无懈可击且y的无懈可击生效了 
						return 0;//如果y的无懈可击生效了,这张就无效了 
					else 
						return 1;//否则这张就生效了 
				}
			}
		}
		else if(identity[x]+predict[to]==3 && k==1)//如果x与to身份不同且这张锦囊牌将有利于to 
		{
			for(int j=0;j<player[x].size();j++)//和上面部分同理 
			{
				if(player[x][j]=='J')
				{
					player[x].erase(player[x].begin()+j);
					predict[x]=identity[x];
					if(J(x,to,0))
						return 0;
					else 
						return 1;
				}
			}
		}
	}
	return 0;//没人有无懈可击 
}

//南蛮入侵 
void N(int from,int to)//from打出的南蛮此时正在对to生效 
{
	if(J(from,to,0))//有人无懈吗? 
	{
		return;//有人无懈了 
	}
	
	bool flag=0;
	for(int i=0;i<player[to].size();i++)//to有杀吗? 
	{
		if(player[to][i]=='K')
		{
			player[to].erase(player[to].begin()+i);
			flag=1;//to有杀 
			break;
		}
	}
	if(flag==0)hp[to]--;//to没有杀,掉血了 
	if(hp[to]==0)//如果没血了怎么办? 
	{
		dying(from,to);//猎杀时刻 
	}
	if(flag==0 && to==king && predict[from]==0)//如果to是主公并且南蛮让他掉血了 
	{
		lei[from]=1;//主公认为from是类反贼 
	}
}

//万箭齐发 
void W(int from,int to)//与上面同理 
{	
	if(J(from,to,0))
	{
		return;
	}
	
	bool flag=0;
	for(int i=0;i<player[to].size();i++)
	{
		if(player[to][i]=='D')//把杀换成闪 
		{
			player[to].erase(player[to].begin()+i);
			flag=1;
			break;
		}
	}
	if(flag==0)hp[to]--;
	if(hp[to]==0)
	{
		dying(from,to);
	}
	if(flag==0 && to==king && predict[from]==0)
	{
		lei[from]=1;
	}
}

//决斗 
void F(int from,int to)//from对to决斗 
{
	if(from!=king)//如果from不是主公,只有主公才会对同阵营的人决斗 
		predict[from]=3-predict[to];//from的阵营与to相反(因为只有亮身份的人才会被决斗,predict[to]一定有值) 
	if(J(from,to,0))return;//无懈可击判断 
	
	if(from==king && identity[to]==1)//如果发起人是主公,目标是忠臣,忠臣不会出杀稳定掉一血(忠臣不会反过来决斗主公,而且这里是identity不是predict) 
	{
		hp[to]--;
		if(hp[to]==0)
			dying(from,to);//濒死 
		return;
	}
	
	bool flag1,flag2;
	while(1)
	{
		flag1=0;
		for(int i=0;i<player[to].size();i++)//to有杀吗 
		{
			if(player[to][i]=='K') 
			{
				player[to].erase(player[to].begin()+i);
				flag1=1;//有了 
				break;
			}
		}
		if(flag1==0)//没有 
		{
			hp[to]--;
			if(hp[to]==0)
				dying(from,to);
			break;
		}
		
		flag2=0;
		for(int i=0;i<player[from].size();i++)//from有杀吗 
		{
			if(player[from][i]=='K')
			{
				player[from].erase(player[from].begin()+i);
				flag2=1;//有了 
				break;
			}
		}
		if(flag2==0)//没有 
		{
			hp[from]--;
			if(hp[from]==0)
				dying(to,from);
			break;
		}
	}
}

//杀 
void K(int from,int to)//from对to出杀 
{
	if(from!=king)//from亮身份 
		predict[from]=3-predict[to];
	bool flag=0;
	for(int i=0;i<player[to].size();i++)//to有闪吗 
	{
		if(player[to][i]=='D')//有了 
		{
			player[to].erase(player[to].begin()+i);
			flag=1;
			break;
		}
	}
	if(flag==0)hp[to]--;//没有 
	if(hp[to]==0)
	{
		dying(from,to);//濒死 
	}
}

int rounduse(int p,int x)//玩家p"尝试"打出他的第x+1张牌,即player[p][x] ,打出了返回1,否则返回0 
{
	switch(player[p][x])
	{
		case 'P'://桃 
			if(hp[p]<4)//没满血就用 
			{
				player[p].erase(player[p].begin()+x);
				hp[p]++;
				return 1;
			}
			else//满血了不出 
				return 0;
		case 'D'://闪(永远出不了) 
			return 0;
		case 'K'://杀(自己唯一能杀到的那个人是敌人吗) 
			if( (identity[p]+predict[dis[p]]==3 ||  (p==king && lei[dis[p]]==1 && predict[dis[p]]==0 /*类反贼的标记只有在没亮身份才有用*/) ) && (Klimit==0 || equip[p]==1/*诸葛连弩?*/))
			{
				Klimit=1;//出杀限制 
				player[p].erase(player[p].begin()+x);
				K(p,dis[p]);
				return 1;
			}
			else
				return 0;
		case 'F'://决斗 
			if(identity[p]==2)//如果p是反,题目设定他会尽可能去打主 
			{
				player[p].erase(player[p].begin()+x);
				F(p,king);
				return 1;
			}
			for(int i=p+1;i<n+p;i++)//逆时针找到第一个敌人 
			{
				if(identity[p]+predict[seat[i]]==3 || (p==king && lei[seat[i]]==1 && predict[seat[i]]==0) )
				{
					if(dead[seat[i]])continue;
					player[p].erase(player[p].begin()+x);
					F(p,seat[i]);
					return 1;
					break;
				}
			}
			return 0;
		case 'N'://南蛮入侵 
			player[p].erase(player[p].begin()+x);
			for(int i=p+1;i<n+p;i++)//逆时针遍历,每个人都会被打到 
			{
				if(dead[seat[i]])continue;
				N(p,seat[i]);
				if(gameover)return 1;//!!!注意如果南蛮在执行过程中游戏结束,那么必须让南蛮停止生效!!! 
			}
			return 1;
		case 'W'://万箭齐发,与南蛮同理 
			player[p].erase(player[p].begin()+x);
			for(int i=p+1;i<n+p;i++)
			{
				if(dead[seat[i]])continue;
				W(p,seat[i]);
				if(gameover)return 1;
			}
			return 1;
		case 'J'://无懈可击不能主动打出 
			return 0;
		case 'Z'://诸葛连弩直接无脑装
			player[p].erase(player[p].begin()+x);
			equip[p]=1;
			return 1;
	}
}

void round(int p)//玩家p的回合 
{
	Klimit=0;//更新出杀限制 
	if(dead[p])return;//如果死了那就没事了 
	
	get_card(p,2);//摸牌阶段 
	
	//从最左边开始搜索手牌,如果不能出就看下一张,如果打出了就从头搜索 
	int k=-1;
	while(1)
	{
		k++;
		if(k==player[p].size())break;
		if(rounduse(p,k))
		{
			k=-1;
		}
		if(dead[p])return;//!!!注意:如果在出牌过程中玩家打出决斗把自己整死了之后要停止出牌!!! 
		if(gameover)return;//游戏结束了也要停止出牌 
	}
}

int main()
{
	cin>>n>>m;
	
	for(int i=1;i<=n;i++)//初始化 
	{
		seat[i]=seat[n+i]=i;
	}
	
	string s;char c;
	for(int i=1;i<=n;i++)//1表示主忠阵营 2表示反贼阵营 
	{
		hp[i]=4;
		cin>>s;
		if(s[0]=='M')identity[i]=1,king=i;
		else if(s[0]=='Z')identity[i]=1;
		else if(s[0]=='F')identity[i]=2,fnum++;
		for(int j=1;j<=4;j++)//初始四张牌 
		{
			cin>>c;
			player[i].push_back(c);
		}
	}
	for(int i=1;i<=m;i++)//摸牌堆 
	{
		cin>>c;
		card.push(c);
	}
	predict[king]=1;//!!!注意:主公的身份是公布的!!! 
	update_dis();//更新距离 
	
	int tot=0;
	while(1)//开始回合循环 
	{
		tot++;
		if(tot==n+1)tot=1;
		round(tot);
		if(gameover)break;
	}
	if(gameover==1)//输出即可 
	{
		cout<<"MP\n";
	}
	else 
		cout<<"FP\n";
	for(int i=1;i<=n;i++)
	{
		if(dead[i])
		{
			cout<<"DEAD\n";
			continue;
		}
		for(int j=0;j<player[i].size();j++)
		{
			cout<<player[i][j]<<" ";
		}
		cout<<"\n";
	}
}

共 3 条回复

Ambel

%%%%%%%%%%%%%%%%%%%

Laffey

奆佬%%%%%%%%%%%%%%%

xing_ling

417行

大小8.86kb