天天看點

【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;
}                

轉載于:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1972.html