P2482 [SDOI2010]猪国杀

连写带调歪歪了一天半。。。。。。

注意理解题意上的坑点:

1、类反猪在未亮明身份时,只有主猪会对其表敌意,其他反猪均将其看作未亮明身份的猪,不会对其表敌意or献殷勤。

2、献殷勤or表敌意的对象只会是已亮明身份的猪(顺便注意:第一次献殷勤or表敌意的玩家身份就亮明了)。

3、关于无懈的结算和使用顺序:

(题目原话)每次有一张锦囊即将生效时,从使用这张锦囊的猪开始,按照逆时针顺序,依次得到使用无懈可击的机会;

举个栗子:

某玩家A出了张万箭,玩家B询问是否有人愿意替他出无懈挡,这个询问先从A开始。如果这时A出了无懈,关于A的无懈的询问(即是否有玩家出无懈抵消A的无懈)应从A逆时针往下第一位询问。

4、玩家x->y的距离并不一定等于玩家y->x的距离(原题题干:距离:两只猪的距离定义为沿着逆时针方向间隔的猪数+1)

 

理解好题意后,我们想一想怎么写。

1、维护玩家之间的敌对关系:

我是用一个数组who记录每一个人的真实身份(主:1 忠:2 反:3),又用一个数组ming记录当前玩家的真实身份。未表明身份ming值=0,否则=该玩家的who值。对于主公认定的未表明身份的“类反猪”,单独开一个killz数组来记。killz=1ming=0的玩家即为“类反猪”。

两个玩家之间的关系判定利用以上三个数组数组暴力判即可。

2、对于锦囊牌:

void 函数名(使用锦囊玩家编号x)
{
    for (i:除x之外的所有玩家)
        if  玩家i符合使用条件
            对玩家i询问无懈
            无懈成功:(决斗)return 
           (南猪or万箭)continue;
            无懈失败:
            出杀or闪环节
            判定减血
}

3、杀的出法和上面差不多。具体细节见代码。

4、无懈的判定:我是用两个函数维护的。

int wuxie(int x)
//询问x的无懈可击是否成功(注意:从使用无懈的人的逆时针方向下一位开始) 无懈成功返回1,失败返回0 
{
    int now=x,flag=0;
    for(ri i=1;i<=n-1;i++)
    {
        now++;
        if(now>n)  now=1;
        if(dead[now])  continue;
        int pla=hadwuxie(now);
        if(!pla)  continue;
        if(ming[x]==0&&who[now]==1&&killz[now]==1)
        {
            if(!ming[now])  ming[now]=who[now];
           	card[now][pla]='0';
            flag=wuxie(now);
           	return flag^1;//无懈成功or失败利用异或维护 
       	}
       	if(ming[x]==1&&who[now]==3)
       	{
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag^1;			
        }
        if(ming[x]==2&&who[now]==3)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag^1;			
        }
        if(ming[x]==3)
        {
            if(who[now]==2||who[now]==1)
            {
                if(!ming[now])  ming[now]=who[now];
                card[now][pla]='0';
                flag=wuxie(now);
                return flag^1;
            }
        }
    }
    return 1;
}

int attack(int x,int from)//询问被锦囊攻击的一方是否有无懈替其抵挡。抵挡成功返回1,失败返回0 
//注意:对于锦囊的被攻击方,询问无懈从使用南猪or决斗or万箭的一方开始 
{
    int now=from-1,flag=0;
    for(ri i=1;i<=n;i++)
    {
        now++;
        if(now>n)  now=1;
        if(dead[now])  continue;
        int pla=hadwuxie(now);
        if(!pla)  continue;	
        if(ming[x]==1&&who[now]<=2)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;			
        }
        if(ming[x]==2&&who[now]<=2)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;			
        }
        if(ming[x]==3&&who[now]==3)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;
        }	
    }
    return 0;
}

剩下的就全是细节了(我的细节上的锅有点多了emmm)

Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#define ri register int 
using namespace std;

const int NMAXN=15,MMAXN=2020;
int n,m,mtot,who[NMAXN],cardtot[NMAXN],cardhad[NMAXN],blood[NMAXN],akhad[NMAXN],tmptot;
int shanow[NMAXN],ming[NMAXN],killz[NMAXN],dead[NMAXN],result;
char ss[MMAXN],card[NMAXN][MMAXN],tmp[MMAXN];
string whonow;

int pd()//判断某局面是否有一方满足胜利条件  ok 
{
    if(dead[1]==1)	return 2;//主死反胜result=2
    int totz=0,totf=0;
    for(ri i=1;i<=n;i++)
        if(!dead[i])
        {
            if(who[i]<=2)	totz++;
            if(who[i]==3)	totf++;
        }
    if(totz==0)	 return 2;//反胜result=2
    if(totf==0)	 return 1;//主胜result=1
    return 0;
}

void jiesuan()//游戏结束时的结算  ok
{
    if(result==1)  cout<<"MP"<<'\n';
    if(result==2)  cout<<"FP"<<'\n';
    for(ri i=1;i<=n;i++)
    {
        if(dead[i])  { cout<<"DEAD"<<'\n'; continue; }
        for(int j=1;j<=cardtot[i];j++)
            if('A'<=card[i][j]&&card[i][j]<='Z')	cout<<card[i][j]<<" ";
        cout<<'\n';
    }
}

int dis(int x,int y)//计算两人的距离 (注意x->y和y->x的距离是不一个意义的) 
{
    int tot=0,now=x;
    if(x<y)
    {
        for(ri i=x+1;i<=y-1;i++)
            if(!dead[i])  tot++;
        return tot+1;
    }
    for(ri i=1;i<=n-(x-y+1);i++)
    {
        now++;
        if(now>n)	now=1;
        if(!dead[now])  tot++;
    }
    return tot+1;
}

int hadsha(int x)//玩家是否有杀  有返回1,无返回0
{
    for(ri k=1;k<=cardtot[x];k++)	
        if(card[x][k]=='K')  { card[x][k]='0'; return 1; }		
    return 0;	
}

int hadshan(int x)//玩家是否有闪  有返回1,无返回0
{
    for(ri k=1;k<=cardtot[x];k++)	
        if(card[x][k]=='D')  { card[x][k]='0'; return 1; }	
    return 0;
}

int hadwuxie(int x)//玩家是否有无懈可击  有返回1,无返回0
{
    for(ri k=1;k<=cardtot[x];k++)	
        if(card[x][k]=='J')  return k;	
    return 0;
}

int tao(int x)//濒死状态下吃桃 成功:0 失败:1 
{
    for(ri i=1;i<=cardtot[x];i++)
    {
        if(card[x][i]=='P')
        {
            card[x][i]='0',blood[x]++;
            if(blood[x]>0)  break;
        }
    }
    if(!blood[x])	return 1;
    return 0;
}

void mopai(int x,int tot)//玩家摸牌  ok 
{
    for(ri i=1;i<=tot;i++)
    {
        if(mtot<m)	mtot++;
        card[x][++cardtot[x]]=ss[mtot];
    }
}

void jianxie(int x,int from)//某玩家被减了一血,结算  x:减血者 from:伤害来源   ok
{
    blood[x]-=1;
    if(blood[x]==0)
    {
        int flag=tao(x);
        if(flag)
        {
            dead[x]=1;
            result=pd();
            if(result) { jiesuan(); exit(0); }
            if(who[x]==2&&who[from]==1)  { cardtot[from]=0; akhad[from]=0; return; } 
            //调了半个小时的Bug;主杀忠后装备牌也应弃置(akhad[from]=0;) 
            if(who[x]==3)  mopai(from,3);
        }
    }
}

int checkene(int x,int y)//检查x是否会判定y为敌人(是1否0)  ok
{
    if(ming[y]==0&&killz[y]==0)  return 0;
    if(who[x]==1)
    {
        if(ming[y]==2)	 return 0;
        if(ming[y]==0&&killz[y]==1)  return 1;
    }
    if(who[x]==2&&ming[y]==1)   return 0;
    if(who[x]==2&&ming[y]==2)   return 0;		
    if(who[x]==3&&ming[y]==3)   return 0;
    return 1;
}

void sha(int x,int pla)//杀
{
    int now=x,flag=0;
    if((!akhad[x])&&shanow[x]==1)	 return;
    if(who[x]==3&&dis(x,1)==1)
    {
        card[x][pla]='0',shanow[x]+=1,cardhad[x]++; //yes:标记该张杀有没有出 
        //注意:杀在出出去后才标记该人使用杀的次数+1(shanow[x]+=1) 
        if(!ming[x]||ming[x]!=who[x])  ming[x]=who[x];
        flag=hadshan(1);//flag:标记该张杀有没有杀到人 
        if(!flag)  jianxie(1,x);
        return;
    }
    for(ri i=1;i<=n-1;i++)
    {
        now++;
        if(dead[now])  continue;
        if(now>n)  now=1;
        if(!checkene(x,now)||dis(x,now)!=1)  continue;
        card[x][pla]='0',shanow[x]+=1,cardhad[x]++; 
        if(!ming[x]||who[x]!=ming[x])  ming[x]=who[x];
        flag=hadshan(now);//flag:标记该张杀有没有杀到人 
        if(!flag)  jianxie(now,x);
        if(card[x][pla]=='0')  break;
    }
    return;
}

int wuxie(int x)
//询问x的无懈可击是否成功(注意:从使用无懈的人的逆时针方向下一位开始) 无懈成功返回1,失败返回0 
{
    int now=x,flag=0;
    for(ri i=1;i<=n-1;i++)
    {
        now++;
        if(now>n)  now=1;
        if(dead[now])  continue;
        int pla=hadwuxie(now);
        if(!pla)  continue;
        if(ming[x]==0&&who[now]==1&&killz[now]==1)
        {
            if(!ming[now])  ming[now]=who[now];
           	card[now][pla]='0';
            flag=wuxie(now);
           	return flag^1;//无懈成功or失败利用异或维护 
       	}
       	if(ming[x]==1&&who[now]==3)
       	{
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag^1;			
        }
        if(ming[x]==2&&who[now]==3)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag^1;			
        }
        if(ming[x]==3)
        {
            if(who[now]==2||who[now]==1)
            {
                if(!ming[now])  ming[now]=who[now];
                card[now][pla]='0';
                flag=wuxie(now);
                return flag^1;
            }
        }
    }
    return 1;
}

int attack(int x,int from)//询问被锦囊攻击的一方是否有无懈替其抵挡。抵挡成功返回1,失败返回0 
//注意:对于锦囊的被攻击方,询问无懈从使用南猪or决斗or万箭的一方开始 
{
    int now=from-1,flag=0;
    for(ri i=1;i<=n;i++)
    {
        now++;
        if(now>n)  now=1;
        if(dead[now])  continue;
        int pla=hadwuxie(now);
        if(!pla)  continue;	
        if(ming[x]==1&&who[now]<=2)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;			
        }
        if(ming[x]==2&&who[now]<=2)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;			
        }
        if(ming[x]==3&&who[now]==3)
        {
            if(!ming[now])  ming[now]=who[now];
            card[now][pla]='0';
            flag=wuxie(now);
            return flag;
        }	
    }
    return 0;
}

void juedou(int x,int pla)//决斗
{
    int flag=0,now=x;
    for(ri i=1;i<=n-1;i++)
    {
        now++;
        if(now>n)	now=1;
        if(checkene(x,now)&&(!dead[now]))	{ flag=1; break; }
    }
    if(who[x]==3)	flag=1,now=1;
    if(!flag)  return;
    card[x][pla]='0',cardhad[x]++; 
    if(!ming[x]||who[x]!=ming[x])   ming[x]=who[x];//亮明身份 
    flag=attack(now,x);
    if(flag)	return;
    if(who[now]==2&&who[x]==1)  { jianxie(now,x); return; }
    int had1=hadsha(now),had2;//被杀对象will先出杀 
    if(!had1)  { jianxie(now,x); return; }
    while(1)//交替减血 
    {
        had1=hadsha(x);
        if(!had1)  { jianxie(x,now); return; }
        had2=hadsha(now);
        if(!had2)  { jianxie(now,x); return; }
    }
    return;
}

void nanzhu(int x)//南猪入侵
{
    int now=x,flag;
    for(ri i=1;i<=n-1;i++)//i:被南猪入侵的玩家 
    {
        now++;
        if(now>n)	now=1;
        if(dead[now])  continue;
        flag=attack(now,x);
        if(flag)  continue;
        flag=hadsha(now);
        if(!flag)
        {
            jianxie(now,x);
            if(!ming[x]&&now==1)  killz[x]=1;
        }
    }
}

void wanjian(int x)//万箭齐发
{
    int now=x,flag;
    for(ri i=1;i<=n-1;i++)//i:i:被万箭袭击的玩家 
    {
        now++;
        if(now>n)	now=1;
        if(dead[now])  continue;//时刻注意不结算已经gg的玩家 
        flag=attack(now,x);
        if(flag)  continue;
        flag=hadshan(now);
        if(!flag)
        {
            jianxie(now,x);
            if(!ming[x]&&now==1)  killz[x]=1;
        }
    }
}

void work()//模拟回合进行函数
{
    while(1)
    {
        for(ri k=1;k<=n;k++)//k:当前出牌的人的编号 
        {
            if(dead[k])  continue;//gg的玩家不出牌 
            mopai(k,2);//摸牌 
            shanow[k]=0,cardhad[k]=1;//清空当前玩家出杀的次数 
            while(cardhad[k]>0)
            {
                if(dead[k])  break;//玩家有可能在使用牌的过程中挂掉(比如一血用决斗结果被干)
                cardhad[k]=0;
                for(ri i=1;i<=cardtot[k];i++)//牌的编号 
                {
                    if(dead[k])  break;//玩家有可能在使用牌的过程中挂掉(比如一血用决斗结果被干)
                    if(card[k][i]=='P'&&blood[k]<4)  { blood[k]++; card[k][i]='0'; cardhad[k]++; }//桃
                    if(card[k][i]=='K')  sha(k,i);//杀 
                    if(card[k][i]=='F')  juedou(k,i);//决斗 
                    if(card[k][i]=='N')  { card[k][i]='0'; nanzhu(k); cardhad[k]++; }//南猪入侵  
                    if(card[k][i]=='W')  { card[k][i]='0'; wanjian(k); cardhad[k]++; }//万箭齐发 
                    if(card[k][i]=='Z')	 { card[k][i]='0'; akhad[k]=1; cardhad[k]++; }//装AK 
                }
            }
            tmptot=0;
            for(ri i=1;i<=cardtot[k];i++)
                if('A'<=card[k][i]&&card[k][i]<='Z')	tmp[++tmptot]=card[k][i];
            for(ri i=tmptot+1;i<=cardtot[k];i++)  card[k][i]='0';
            for(ri i=1;i<=tmptot;i++)	card[k][i]=tmp[i];
            cardtot[k]=tmptot;
        }
    }
}

void shuru()//初始化信息函数
{
    scanf("%d%d",&n,&m);
    for(ri i=1;i<=n;i++)
    {
        cin>>whonow;
        if(whonow=="MP")	who[i]=1;
        if(whonow=="ZP")	who[i]=2;
        if(whonow=="FP")	who[i]=3;
        for(ri j=1;j<=4;j++)	cin>>card[i][j];//初始化卡牌 
        cardtot[i]=4,blood[i]=4;//初始化血量,卡牌数
    }
    for(ri i=1;i<=m;i++)	cin>>ss[i];//初始化牌堆
    ming[1]=1;//表示某人是否亮明身份 
}

int main()//主函数
{
    shuru();
    work();
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值