zoukankan      html  css  js  c++  java
  • [洛谷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
    ";else cout<<"MP
    ";for(int i=1;i<=n;i++){if(p[i].H<=0)cout<<"DEAD
    ";
    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
    ";//MP死了FP胜 
    	else cout<<"MP
    ";//否则MP胜 
    	for(int i=1;i<=n;i++){//输出最终信息 
    		if(p[i].HP<=0)cout<<"DEAD
    ";
    		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分。
    当时我的心情可以用百感交集来形容,不过我并没有表现出来什么,因为机房里还有一位初三巨佬。我只是笑了笑,截下了这个历史性时刻,也算是没有辜负自己的努力了。

  • 相关阅读:
    【HDU 2093】考试排名(结构体水题)
    【HDU 2037】今年暑假不AC
    【HDU 1234】开门人和关门人(水题)
    【HDU 1005】Number Sequence
    第一篇博客——ACM之路!
    深度学习全家福
    搭建 keras + tensorflow
    MSCI 成份股 清单
    SK-Learn 全家福
    创业笔记 -- 网站正式对外运营
  • 原文地址:https://www.cnblogs.com/juruoajh/p/14196658.html
Copyright © 2011-2022 走看看