0.Description
1.Solution
此题是一道综合了各种基础算法的难题,主要考验选手对 C++ 语言的掌握程度。
不口胡了,直接说注意事项:
1.仔细读题!仔细读题!仔细读题!(至少读五遍!)
2.注意递归无懈的写法;
3.注意时刻判断有没有猪死亡、游戏是否结束;
4.注意break
和continue
的使用;
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分。
当时我的心情可以用百感交集来形容,不过我并没有表现出来什么,因为机房里还有一位初三巨佬。我只是笑了笑,截下了这个历史性时刻,也算是没有辜负自己的努力了。