[洛谷P2482][题解][SDOI2010]猪国杀

0.Description

好 长 啊

1.Solution

此题是一道综合了各种基础算法的难题,主要考验选手对 C++ 语言的掌握程度。
不口胡了,直接说注意事项:
1.仔细读题!仔细读题!仔细读题!(至少读五遍!)
2.注意递归无懈的写法;
3.注意时刻判断有没有猪死亡、游戏是否结束;
4.注意breakcontinue的使用;
5.有些牌使用后可能会有其他牌可以使用,应该重新遍历手牌;
6.分清楚每种猪的朋友和敌人,类反猪并没有跳身份;
7.注意猪的信息初始化;
8.注意处理前后关系,有猪死亡要改变前后关系;
⑨.注意真实身份和表现身份的区别;
10.数据有 bug ,抽完牌堆要摸最后一张牌;
11. ……(各位自己探索吧)

2.Code

#include<bits/stdc++.h>
#define ct continue
#define R return
#define U p[i].c[j]='U'
using namespace std;int n,m,FP;bool G;char a[2010],s[20];struct Pig{bool W;char c[2010],I;int C,H,L,N;}p[20];
void g(int x){if(!m)m++;p[x].c[++p[x].C]=a[m--];}
void D(int x,int y){for(int i=1;i<=p[x].C;i++)if(p[x].c[i]=='P'){p[x].c[i]='U',p[x].H++;R;}p[p[x].L].N=p[x].N,p[p[x].N].L=p[x].L;
if(x==1){G=1;R;}if(p[x].I=='F')FP--;if(!FP){G=1;R;}if(p[x].I=='F')g(y),g(y),g(y);
if(p[x].I=='Z'&&p[y].I=='M')p[y].C=0,p[y].W=0;}void jp(int x){s[x]=p[x].I;}
void K(int x,int y){for(int i=1;i<=p[y].C;i++)if(p[y].c[i]=='D'){p[y].c[i]='U';R;}if(!--p[y].H)D(y,x);}bool J(int x,int y,int opt){int i=x;
while(1){if(opt){if(s[y]==p[i].I||(s[y]=='M'&&p[i].I=='Z')||(s[y]=='Z'&&p[i].I=='M')){
for(int j=1;j<=p[i].C;j++)if(p[i].c[j]=='J'){U,jp(i);R !J(i,x,0);}}}else{
if(((p[i].I=='M'||p[i].I=='Z')&&s[x]=='F')||(p[i].I=='F'&&(s[x]=='M'||s[x]=='Z'))){
for(int j=1;j<=p[i].C;j++)if(p[i].c[j]=='J'){U,jp(i);R !J(i,x,0);}}}
i=p[i].N;if(i==x)break;}R 0;}void N(int x){for(int y=p[x].N;y!=x;y=p[y].N){if(!J(x,y,1)){int i;
for(i=1;i<=p[y].C;i++)if(p[y].c[i]=='K'){p[y].c[i]='U';break;}if(i>p[y].C){if(y==1&&s[x]=='U')s[x]='L';
if(!--p[y].H)D(y,x);if(G)R;}}}}void W(int x){for(int y=p[x].N;y!=x;y=p[y].N){ 
if(!J(x,y,1)){int i;for(i=1;i<=p[y].C;i++)if(p[y].c[i]=='D'){p[y].c[i]='U';break;}
if(i>p[y].C){if(y==1&&s[x]=='U')s[x]='L';if(!--p[y].H)D(y,x);if(G)R;}}}}void F(int x,int y){if(J(x,y,1))R;
if(x==1&&p[y].I=='Z'){if(!--p[y].H)D(y,x);R;}int i=1,j=1;while(1){while(p[y].c[i]!='K'&&i<=p[y].C)i++; 
if(i>p[y].C){p[y].H--;if(!p[y].H)D(y,x);R;}else p[y].c[i]='U';while(p[x].c[j]!='K'&&j<=p[x].C)j++;
if(j>p[x].C){p[x].H--;if(!p[x].H)D(x,y);R;}else p[x].c[j]='U';}}void S(){G=1;if(FP)G=0;if(G)R;for(int i=1;i;i=p[i].N){g(i),g(i);bool cuk=1;
for(int j=1;j<=p[i].C;j++){if(p[i].c[j]!='U'){if(!p[i].H)break;
char x=p[i].c[j];if(x=='P'){if(p[i].H!=4)p[i].H++,U;ct;}if(x=='K'){if(!cuk&&!p[i].W)ct;
if((p[i].I=='M'&&s[p[i].N]!='F'&&s[p[i].N]!='L')||(p[i].I=='Z'&&s[p[i].N]!='F')||(p[i].I=='F'&&s[p[i].N]!='M'&&s[p[i].N]!='Z'))ct;
U,K(i,p[i].N),jp(i),cuk=0;if(G)R;ct;}if(x=='F'){if(p[i].I=='F'){U,F(i,1),jp(i);if(G)R;j=0;ct;}
for(int k=p[i].N;k!=i;k=p[k].N){if(s[k]=='F'||(p[i].I=='M'&&s[k]=='L')){U,F(i,k),jp(i);if(G)R;j=0;break;}}}
if(x=='N'){U,N(i);if(G)R;j=0;ct;}if(x=='W'){U;W(i);if(G)R;j=0;ct;}if(x=='Z'){U;p[i].W=1;j=0;ct;}}}}}
int main(){cin>>n>>m;for(int i=1;i<=n;i++)p[i].L=i-1,p[i].N=i+1;
p[1].L=n,p[n].N=1;for(int i=1;i<=n;i++){for(int j=1;j<2010;j++)U;
p[i].C=p[i].H=4,p[i].W=0,s[i]='U';char l[5];scanf("%s",l),p[i].I=l[0];
for(int j=1;j<=4;j++)scanf("%s",l),p[i].c[j]=l[0];if(p[i].I=='F')FP++;}s[1]='M';
for(int i=1;i<=m;i++){char l[5];scanf("%s",l);a[m-i+1]=l[0];}S(); 
if(p[1].H<=0)cout<<"FP\n";else cout<<"MP\n";for(int i=1;i<=n;i++){if(p[i].H<=0)cout<<"DEAD\n";
else{for(int j=1;j<=p[i].C;j++)if(p[i].c[j]!='U')cout<<p[i].c[j]<<" ";puts("");}}R 0;}

啊对不起放错了(
正常的代码:

#include<bits/stdc++.h>
#define LL long long
#define register
#define inline
#define INF 0x3f3f3f3f
using namespace std;
inline void Read(int &x){
	int f=1;
	char c=getchar();
	x=0;
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	x*=f;
}
int n,m;
struct Pig {
	bool Weapon;//是否装备武器  
	char Card[2010];//当前卡牌 PKDFNWJZU
	char Id;//身份 MZF 
	int Cards;//卡牌数量 
	int HP;//生命值 
	int lst,nxt;//前驱后继 
}p[20];
bool GameOver;//游戏结束 
char AllCards[2010];//牌堆
char Seem[20];//MP眼中的身份 MZFUL
int FP;//反猪数量
void Get(int now){//摸牌 
	if(!m)m++;
	p[now].Card[++p[now].Cards]=AllCards[m--];
}
void Dead(int now,int killer){//处理濒死状态的猪 
	for(int i=1;i<=p[now].Cards;i++){//如果吃桃能活过来就吃 
		if(p[now].Card[i]=='P'){
			p[now].Card[i]='U',p[now].HP++;
			return;
		}
	}
	p[p[now].lst].nxt=p[now].nxt;//处理相邻关系 
	p[p[now].nxt].lst=p[now].lst;
	if(now==1){//MP死了游戏结束 
		GameOver=1;
		return;
	}
	if(p[now].Id=='F')FP--;//FP死了要统计 
	if(!FP){//没有FP游戏结束 
		GameOver=1;
		return;
	}
	if(p[now].Id=='F'){//杀死FP摸三张牌 
		Get(killer),Get(killer),Get(killer);
	}
	if(p[now].Id=='Z'&&p[killer].Id=='M'){//MP杀ZP要弃牌 
		p[killer].Cards=0,p[killer].Weapon=0;
	}
}
void Jump(int now){//跳忠/跳反 
	Seem[now]=p[now].Id;
}
void K(int now,int to){//使用【杀】 
	for(int i=1;i<=p[to].Cards;i++){
		if(p[to].Card[i]=='D'){//有【闪】就弃置 
			p[to].Card[i]='U';
			return;
		}
	}
	p[to].HP--;//我大意了啊,没有闪 
	if(!p[to].HP)Dead(to,now);
}
#define Unsuccessful 0
bool J(int now,int to,int opt){//使用【无懈可击】 
//opt==0:now无懈掉to的无懈
//opt==1:now无懈掉打到to的锦囊牌 
	int i=now;
	while(1){
		if(opt==1){
			//同伙,帮助无懈掉 
			if(Seem[to]==p[i].Id||(Seem[to]=='M'&&p[i].Id=='Z')||(Seem[to]=='Z'&&p[i].Id=='M')){
				for(int j=1;j<=p[i].Cards;j++){
					if(p[i].Card[j]=='J'){
						p[i].Card[j]='U';
						Jump(i);
						return !J(i,now,0);//i是否无懈成功 
					}
				}
			}
		}else {
			//敌人,无懈掉他的无懈 
			if(((p[i].Id=='M'||p[i].Id=='Z')&&Seem[now]=='F')||(p[i].Id=='F'&&(Seem[now]=='M'||Seem[now]=='Z'))){
				for(int j=1;j<=p[i].Cards;j++){
					if(p[i].Card[j]=='J'){
						p[i].Card[j]='U';
						Jump(i);
						return !J(i,now,0);//i是否无懈成功 
					}
				}
			}
		}
		i=p[i].nxt;
		if(i==now)break;
	}
	return Unsuccessful;//无懈失败 
}
#undef Unsuccessful
void N(int now){//使用【南猪入侵】 
	for(int to=p[now].nxt;to!=now;to=p[to].nxt){//寻找目标 
		if(!J(now,to,1)){//没有被无懈 
			int i;
			for(i=1;i<=p[to].Cards;i++){//需要弃置【杀】 
				if(p[to].Card[i]=='K'){
					p[to].Card[i]='U';
					break;
				} 
			}
			if(i>p[to].Cards){//防御失败 
				p[to].HP--;
				if(to==1&&Seem[now]=='U')Seem[now]='L';//打到MP,变成类反猪 
				if(!p[to].HP)Dead(to,now);
				if(GameOver)return;
			}
		}
	}
}
void W(int now){//使用【万箭齐发】 
	for(int to=p[now].nxt;to!=now;to=p[to].nxt){//以下同【南猪入侵】 
		if(!J(now,to,1)){
			int i;
			for(i=1;i<=p[to].Cards;i++){
				if(p[to].Card[i]=='D'){//不同的是要弃置【闪】 
					p[to].Card[i]='U';
					break;
				} 
			}
			if(i>p[to].Cards){
				p[to].HP--;
				if(to==1&&Seem[now]=='U')Seem[now]='L';
				if(!p[to].HP)Dead(to,now);
				if(GameOver)return;
			}
		}
	}
}
void F(int now,int to){//使用【决斗】 
	if(J(now,to,1))return;
	if(now==1&&p[to].Id=='Z'){//特殊情况:MP对ZP,ZP必输 
		p[to].HP--;
		if(!p[to].HP)Dead(to,now);
		return;
	}
	int i=1,j=1;//遍历两人手牌的指针 
	while(1){
		while(p[to].Card[i]!='K'&&i<=p[to].Cards)i++;//从对手开始轮流弃置【杀】 
		if(i>p[to].Cards){//没有【杀】的一方输 
			p[to].HP--;
			if(!p[to].HP)Dead(to,now);
			return;
		}else p[to].Card[i]='U';
		while(p[now].Card[j]!='K'&&j<=p[now].Cards)j++;
		if(j>p[now].Cards){
			p[now].HP--;
			if(!p[now].HP)Dead(now,to);
			return;
		}else p[now].Card[j]='U';
	}
}
void StartRound(){//开始游戏 
	GameOver=1;
	if(FP)GameOver=0; 
	if(GameOver)return;
	for(int i=1;i;i=p[i].nxt){
		Get(i),Get(i);//每个人摸两张牌 
		bool can_use_kill=1;//用【杀】的机会 
		for(int j=1;j<=p[i].Cards;j++){
			if(p[i].Card[j]!='U'){
				if(!p[i].HP)break;
				char now=p[i].Card[j];
				if(now=='P'){//使用【桃】 
					if(p[i].HP!=4)p[i].HP++,p[i].Card[j]='U';
					continue;
				}
				if(now=='K'){//使用【杀】 
					if(!can_use_kill&&!p[i].Weapon)continue;//用过了且没有武器 
					if(p[i].Id=='M'&&Seem[p[i].nxt]!='F'&&Seem[p[i].nxt]!='L')continue;//MP只打FP和LP 
					if(p[i].Id=='Z'&&Seem[p[i].nxt]!='F')continue;//ZP只打FP 
					if(p[i].Id=='F'&&Seem[p[i].nxt]!='M'&&Seem[p[i].nxt]!='Z')continue;//FP只打MP和ZP 
					p[i].Card[j]='U';
					K(i,p[i].nxt);
					Jump(i),can_use_kill=0;
					if(GameOver)return;
					continue;
				}
				if(now=='F'){//使用【决斗】 
					if(p[i].Id=='F'){//FP一定打MP 
						p[i].Card[j]='U';
						F(i,1);
						Jump(i);
						if(GameOver)return;
						j=0;
						continue;
					}
					for(int k=p[i].nxt;k!=i;k=p[k].nxt){//否则找决斗目标FP或LP 
						if((p[i].Id=='M'&&(Seem[k]=='F'||Seem[k]=='L'))||(p[i].Id=='Z'&&Seem[k]=='F')){
							p[i].Card[j]='U',F(i,k);
							Jump(i);
							if(GameOver)return;
							j=0;
							break;
						}
					}
				}
				if(now=='N'){//使用【南猪入侵】 
					p[i].Card[j]='U';
					N(i);
					if(GameOver)return;
					j=0;
					continue;
				}
				if(now=='W'){//使用【万箭齐发】 
					p[i].Card[j]='U';
					W(i);
					if(GameOver)return;
					j=0;
					continue;
				}
				if(now=='Z'){//使用【猪哥连弩】 
					p[i].Card[j]='U';
					p[i].Weapon=1;
					j=0;
					continue;
				}
			}
		}
	}
}
int main(){
	Read(n),Read(m);
	for(int i=1;i<=n;i++)p[i].lst=i-1,p[i].nxt=i+1;//初始化攻击距离 
	p[1].lst=n,p[n].nxt=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<2010;j++)p[i].Card[j]='U';//初始化手牌 
		p[i].Cards=p[i].HP=4;//初始化HP和手牌数 
		p[i].Weapon=0;//初始化武器 
		Seem[i]='U';//初始化表现出的身份 
		char ipt[5];scanf("%s",ipt);//输入身份 
		p[i].Id=ipt[0];
		for(int j=1;j<=4;j++)scanf("%s",ipt),p[i].Card[j]=ipt[0];//输入手牌 
		if(p[i].Id=='F')FP++;//统计FP 
	}
	Seem[1]='M';
	for(int i=1;i<=m;i++){//输入卡牌堆 
		char ipt[5];scanf("%s",ipt);
		AllCards[m-i+1]=ipt[0];
	}
	StartRound();//开始游戏! 
	if(p[1].HP<=0)cout<<"FP\n";//MP死了FP胜 
	else cout<<"MP\n";//否则MP胜 
	for(int i=1;i<=n;i++){//输出最终信息 
		if(p[i].HP<=0)cout<<"DEAD\n";
		else {
			for(int j=1;j<=p[i].Cards;j++){
				if(p[i].Card[j]!='U')cout<<p[i].Card[j]<<" ";
			}
			cout<<endl;
		}
	}
	return 0;
}

3.总结

本题是一道美妙的大模拟,有助于提高大家的读题能力、代码能力及调试能力,但出在省选里着实有点生草(反正我做了超过 10h )

4.花絮

对于此毒瘤题我早有耳闻,但一直不敢下手。在今年8月份的 SDSC 中我利用晚自习的时间对照着题解打出了第一份猪国杀代码,很可惜一直是10分。
考完 NOIp 之后没事干,于是立了个 flag :12月底之前 AC 猪国杀。(当时本来打算12月60号把它 AC 了的)
但是四个月前的代码已然成为一坨屎山,于是我毅然决然打开题解,仔细阅读了巨佬们的代码,还花了一些时间弄懂了恶心的递归无懈。
最终我把代码重构出来了,却一直是65分,我拿来题解代码一遍遍比对却没有发现任何问题。
可就在这周五下午,我发现了!把一个continue轻轻改成break后,单击提交代码,100分。
当时我的心情可以用百感交集来形容,不过我并没有表现出来什么,因为机房里还有一位初三巨佬。我只是笑了笑,截下了这个历史性时刻,也算是没有辜负自己的努力了。

上一篇:堆的代码实现


下一篇:2020-12-21