zoukankan      html  css  js  c++  java
  • 【BZOJ1972】[SDOI2010] 猪国杀(恶心的大模拟)

    点此看题面

    大致题意: 让你模拟一个游戏猪国杀的过程。

    几大坑点

    对于这种模拟题,具体思路就不讲了,就说说有哪些坑点。

    • 题面有锅,反猪是\(FP\)

    • 数据有锅,牌堆中的牌可能不够用,牌堆为空之后需一直抽最后一张牌。

    • 主猪杀死忠猪后猪哥连弩也要清除

    • 无懈可击也可以用无懈可击抵消

    • 使用决斗的猪可能死亡。

    • 无懈可击是从使用锦囊牌的猪开始轮流选择是否响应。

    • 使用完一张牌后(不包括桃)有可能会导致之前跳过的杀或决斗有对象使用,因此要重新扫描一遍。

    • 只有主猪会特别针对类反猪

    • 如果杀死某只反猪后游戏结束,是不摸三张牌的。

    大致就是这些了,我还犯了一些其他十分智障的错误,以至于调到绝望

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1))
    #define hl_AK_NOI true
    #define GetCard(x) (s[x].card[++s[x].k]=cards[cd<m?++cd:m])//摸一张牌,注意牌堆中的牌可能不够用
    using namespace std;
    int n,m,cd=0,Last[15],Next[15];
    char cards[2005];
    class FIO
    {
    	private:
    		#define Fsize 100000
    		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
    		int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    	public:
    		FIO() {FinNow=FinEnd=Fin;}
    		inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
    		inline void read_char(char &x) {while(isspace(x=tc()));}
    		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
    		inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
    		inline void write_char(char x) {pc(x);}
    		inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
    		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    struct Pig//定义结构体,表示一只猪
    {
        int id,Type,hp,status,k;//id记录猪的编号,Type记录猪的类型,hp记录猪的剩余血量,status记录猪当前表明的状态(0:未知;1:主猪;2:忠猪;3:反猪;4:类反猪),k记录手中牌的数量
        char card[5005];//card存储手牌
        bool dead,Z;//dead记录猪是否死亡,Z记录猪是否有猪哥连弩
        Pig(int x=0,string y="NULL"):id(x),Type(y[0]^'M'?(y[0]^'F'?2:3):1)//构造函数
    	{
    		if(y=="NULL") return;//一开始很智障地没写这句话,结果炸飞
    		status=(Type==1),hp=k=4,dead=Z=false;
    		register int i;
            for(i=1;i<=4;++i) F.read_char(card[i]);//读入手牌
            for(i=5;i<=5000;++i) card[i]='\0';
    	}
        inline void Hurt(int);//受到伤害
        inline bool Use(char ch)//查询是否有这张牌,如果有就使用并返回true,否则返回false
        {
        	for(register int i=1;i<=k;++i) if(card[i]==ch) return card[i]='\0',true;//扫描每一张牌,查询是否有这张牌
        	return false;//返回false
    	}
    }s[15];
    inline void End()//游戏结束
    {
        register int i,j;
        for(F.write_string(s[1].dead?"FP\n":"MP\n"),i=1;i<=n;++i) 
    	{
    		if(s[i].dead) {F.write_string("DEAD\n");continue;}//如果死亡输出DEAD
    		for(j=1;j<=s[i].k;++j) if(s[i].card[j]) F.write_char(s[i].card[j]),F.write_char(' ');//输出手牌
    		F.write_char('\n');
    	}
    	F.end(),exit(0);
    }
    inline int GetLast(int x) {return s[Last[x]].dead?Last[x]=GetLast(Last[x]):Last[x];}//查询前一头猪
    inline int GetNext(int x) {return s[Next[x]].dead?Next[x]=GetNext(Next[x]):Next[x];}//查询后一头猪
    inline void Pig::Hurt(int x)//受到来源于编号为x的猪的一点伤害 
    {
    	if(--hp) return;//如果没死,退出函数
    	if(Use('P')) return (void)(++hp);//如果有桃,回一点体力
    	k=0,dead=true;//清空手牌,标记已死亡
        if(Type==1) End();//如果死掉的是主猪,说明游戏结束了
        if(Type==2&&s[x].Type==1) s[x].k=0,s[x].Z=false;//如果是主猪杀死了忠猪,清空主猪手牌,并注意清除猪哥连弩
        Last[GetNext(id)]=Last[id],Next[GetLast(id)]=Next[id];//将该猪从牌局中删去
        if(Type^3) return;//如果死的不是反猪,退出函数
        register int i,t,FP=0;
    	for(i=1,t=0;i^t;i=GetNext(i),t=1) if(s[i].Type==3) {FP=true;break;}//判断是否还有反猪
    	if(!FP) End();//如果没有反猪,说明游戏结束了
    	GetCard(x),GetCard(x),GetCard(x);//摸三张牌
    }
    inline int GetStatus(int x,int y)//判断x与y的关系(1:同伙;2:没有关系;3:敌人)
    {
        if(!s[y].status) return 2;//如果y还没表明身份,返回2
        switch(s[x].Type)//分类讨论(注意除主猪外其他猪与类反猪没有关系)
        {
        	case 1:if(s[y].status==1) return 1;if(s[y].status==2) return 1;if(s[y].status==3) return 3;if(s[y].status==4) return 3;break;
        	case 2:if(s[y].status==1) return 1;if(s[y].status==2) return 1;if(s[y].status==3) return 3;if(s[y].status==4) return 2;break;
        	case 3:if(s[y].status==1) return 3;if(s[y].status==2) return 3;if(s[y].status==3) return 1;if(s[y].status==4) return 2;break;
    	}
    	return 2;
    }
    inline void BeGood(int x,int y)//通过x向y献殷勤,更新x的身份 
    {
    	if((s[y].status==1||s[y].status==2)&&s[x].Type^1) s[x].status=2;//如果y是忠猪或反猪且x不是主猪,说明x是忠猪
    	if(s[y].status==3) s[x].status=3;//如果y是反猪,说明x是反猪
    }
    inline void BeBad(int x,int y)//通过x向y表敌意,更新x的身份 
    {
    	if(s[y].status==1||s[y].status==2) s[x].status=3;//如果y是忠猪或反猪,说明x是反猪
    	if(s[y].status==3&&s[x].Type^1) s[x].status=2;//如果y是反猪且x不是主猪,说明x是忠猪
    }
    inline void K(int x,int y)//x向y打出一张杀 
    {
    	BeBad(x,y);//x向y表敌意了
    	if(!s[y].Use('D')) s[y].Hurt(x);//如果y没有出闪,就受到来自x的一点伤害
    }
    inline bool J(int x,int y,int v)//依次决定对于由x对y打出的一张无懈可击是否相应,其中v表示出无懈可击是献殷勤还是表敌意(1:献殷勤;3:表敌意)
    {
    	register int i,t;
        for(i=x,t=0;i^t;i=GetNext(i),t=x) 
        {
            if(GetStatus(i,y)^v) continue;//如果不是该关系,就跳过
            if(s[i].Use('J'))//如果打出无懈可击 
            {
            	v^1?BeBad(i,y):BeGood(i,y);
    			return !J(i,y,4-v);//递归调用该函数
    		}
        }
        return false;//没有猪打出无懈可击
    }
    inline void Fight(int x,int y)//x向y打出决斗
    {
        register int i1=1,i2=1;
        BeBad(x,y);//x向y表敌意
        if(J(x,y,1)) return;//如果有猪打出无懈可击,退出函数
        if(s[x].Type==1&&s[y].Type==2) return (void)(s[y].Hurt(x));//如果x为主猪,y为忠猪,则必定是y受到伤害
        while(hl_AK_NOI)//两猪轮流出杀
        {
            if(!s[y].Use('K')) return (void)(s[y].Hurt(x));//如果y没杀了,受到来自x的一点伤害
            if(!s[x].Use('K')) return (void)(s[x].Hurt(y));//如果x没杀了,受到来自y的一点伤害
        }
    }
    inline void AOE(int x,char Need)//由x发起的一次范围攻击,Need表示需要打出的牌
    {
    	register int i;
        for(i=GetNext(x);i^x;i=GetNext(i)) 
        {
        	if(J(x,i,1)||s[i].Use(Need)) continue;//如果有猪打出无懈可击,或该猪打出了需要打出的牌,跳过
    		if(s[i].Type==1&&!s[x].status) s[x].status=4;//如果受到伤害的是主猪,且打出范围攻击的猪未表明身份,则标记其为类反猪
    		s[i].Hurt(x);//当前猪受到来自x的一点伤害
    	}
    }
    inline void Work()//出牌
    {
    	register int i,j,kk=0,did=0,used;register char op;static int nw=1;
        for(GetCard(nw),GetCard(nw),i=1;i<=s[nw].k;++i)//摸两张牌 
        {
    		op=s[nw].card[i],s[nw].card[i]='\0';
    		switch(op)
    		{
            	case 'K':
            		if(did&&!s[nw].Z) {s[nw].card[++kk]='K';break;}//如果出过杀,且没有猪哥连弩,跳过
    				if(GetStatus(nw,GetNext(nw))==3) K(nw,GetNext(nw)),did=1,i=kk=0;//如果与下一头猪是敌对关系,则打出杀,并从头开始扫描
    				else s[nw].card[++kk]='K';
    			break;
            	case 'F':
    				if(s[nw].Type==3) {Fight(nw,1),i=kk=0;break;}//如果是反猪,则对主猪打出决斗,并从头开始扫描
    				for(used=0,j=GetNext(nw);j^nw;j=GetNext(j)) if(GetStatus(nw,j)==3) {Fight(nw,j),used=true,i=kk=0;break;}//找到一个敌对关系的猪,打出决斗,并标记已使用,然后从头开始扫描
    				!used&&(s[nw].card[++kk]='F');
    			break;
            	case 'D':s[nw].card[++kk]='D';break;
            	case 'J':s[nw].card[++kk]='J';break;
            	case 'N':AOE(nw,'K'),i=kk=0;break;//打出南猪入侵,并从头开始扫描
    			case 'W':AOE(nw,'D'),i=kk=0;break;//打出万箭齐发,并从头开始扫描
    			case 'P':if(s[nw].hp<4) ++s[nw].hp;else s[nw].card[++kk]='P';break;//如果未满血,回复一点体力
            	case 'Z':s[nw].Z=true,i=kk=0;break;//装备猪哥连弩,然后从头开始扫描
    		}
    		if(!s[nw].k) kk=0;
        }
        s[nw].k=kk,nw=GetNext(nw);
    }
    int main()
    {
    	register int i;register string st;
        for(F.read(n),F.read(m),i=1;i<=n;++i) F.read_string(st),s[i]=Pig(i,st),Last[i]=i-1,Next[i]=i+1;//读入每一头猪的信息,并初始化其前一头猪和后一头猪
        for(Last[1]=n,Next[n]=1,i=1;i<=m;++i) F.read_char(cards[i]);//读入牌堆
        while(hl_AK_NOI) Work();//一直操作
        return F.end(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Python之MySQLdb
    Python 小方法
    Python文件打包
    禅道使用教程
    Linux命令
    安卓自动化测试monkey
    深入分析Java中的中文编码问题
    Linux命令搜索
    文件上传的类型选择控制
    MySql格式化日期函数
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1972.html
Copyright © 2011-2022 走看看