zoukankan      html  css  js  c++  java
  • 【SDOI2010】猪国杀 题解(模拟)

    前言:嗅到了一丝头秃的味道……

    ------------------

    题目链接

    题目实在太长,变量也很多。建议至少读个三五遍再做题。不要忽略任何细节,不要想当然。(因为真正玩三国杀肯定不像猪一样出牌啊……

    总结一些有用的信息:

    1.有主猪,忠猪,反猪三种身份。忠猪和反猪可以有多个。

    2.游戏规则:反猪要杀死主猪,主猪和忠猪要杀死所有反猪。

    3.初始阶段每个人都有四张牌,且初始体力值都是四。

    4.每次摸牌都能摸两张牌,且放到自己手牌的最右边

    5.出牌时每次都使用最靠左的能使用的牌。

    6.如果没有诸葛连弩,每次最多只能使用一张杀。若有诸葛连弩可以使用无数次杀。

    7.当牌被弃置后就与游戏无关。

    8.牌的类型:

      1.桃:每次使用可以恢复一点体力值,体力值最高能到达体力上限。桃只能对自己使用。当处于自己回合外且自己血量小于等于0时也可以使用。

      2.杀:杀的攻击范围为1。可以被闪抵消。可以造成一点伤害。

      3.闪:当受到杀的攻击时可以用闪来抵消。

      4.决斗:可以对除自己外任意一名角色使用。对方先出杀。谁无杀可以出谁掉一滴血。

      5.南蛮入侵:群体伤害。谁不出杀谁掉一滴血。

      6.万箭齐发:群体伤害。谁不出闪谁掉一滴血。

      7.无懈可击:对决斗使用时,该决斗无效。对5/6使用时,该角色可以不出牌而不受到伤害。对无懈可击使用时,该无懈可击无效。

      8.诸葛连弩:装备上可以出无限次杀。

    9.伤害来源:谁使用该牌造成伤害,谁就是伤害来源。

    10.距离:单向。例如1到2是1,而2到1是n-1。

    11.奖励与惩罚:无论谁杀了反贼,都可以摸三张牌。如果主公杀了忠臣,那么主公要弃置所有的牌。

    12.几种行为:

      1.献殷勤:使用无懈可击挡下南蛮入侵,万箭齐发,决斗;使用无懈可击抵消表敌意。

      2.表敌意:对某个角色使用杀/决斗。使用无懈可击抵消献殷勤。

      3.跳忠:对某个对主公跳忠或对已经跳忠的猪跳忠的猪跳忠,或对对已经跳反的猪跳反。

      4.跳反:与3相反。

    13.忠猪不会跳反,反猪也不会跳忠。对所有的猪,能够跳必然跳

    14.对于所有角色:

      1.能够使用桃,必然使用。

      2.有南蛮入侵/万箭齐发,必然使用。

      3.有诸葛连弩必然装上。

      4.受到各种威胁时,有牌必然弃置。

      5.不会对未标明身份的猪献殷勤/表敌意(包括自己)

    15.特性:

      1.主猪:主猪会认为「没有跳身份,且用南猪入侵 / 万箭齐发对自己造成伤害的猪」是类反猪。如果这个猪之后重新跳那么主猪会重新认识这只猪。

      2.忠猪:受到来自主猪的决斗时不会使用杀。

      3.共性:能献殷勤/表敌意那么一定进行。必须是在自己距离范围内的猪

    ----------------------------------

    以上加粗的部分是我在写代码时卡住/出bug的地方。希望后人不再犯这些错误QAQ。

    整篇代码难度最高的地方就是献殷勤/表敌意和无懈可击。我还是参考了题解才写出来这部分。

    先说献殷勤/表敌意。代码中用一个$f(x,y)$函数来表示。对于每只猪,都有一个给定的身份($pid$)和已经跳的身份($id$)。具体如下:

    inline int f(int x,int y)//x-->y献殷勤/表敌意。0表敌意,1献殷勤 
    {
        if (a[y].id==-1) return -1;
        if (a[y].id==3){if (a[x].pid==0) return 0;else return -1;}
        if (a[x].pid==0){if (a[y].id==0||a[y].id==1) return 1;else return 0;}
        if (a[x].pid==1){if (a[y].id==1||a[y].id==0) return 1;else return 0;}
        if (a[x].pid==2){if (a[y].id==1||a[y].id==0) return 0;else return 1;}
        return -1;
    }

    再说无懈可击。无懈可击可以套娃式进行,这是最麻烦的地方。这里要写一个递归再加上各种判断才可以。每次递归后献殷勤/表敌意的关系都会改变。具体看代码:

    inline bool J(int x,int y,int v){//无懈可击 
        if (f(x,y)==v&&have(x,'J')){
            update(x,y,0);
            if (!J(x,y,v^1)) return 1;
        }
        for (int i=a[x].nt;i!=x;i=a[i].nt){
            if (f(i,y)==v&&have(i,'J')){
                update(i,y,0);
                if (!J(i,y,v^1)) return 1;
            }
        }
        return 0;
    }

    剩下的地方都不算难,慢慢写就好。最后挂一下我出错的地方:

    1.没有注意“类反猪”的定义。

    2.没有分清id和pid。

    3.忘记了牌的顺序是不能改变的。

    4.桃乱用。

    5.决斗的伤害来源。

    6.递归和循环写死了……

    7.各种语法、细节错误数不胜数……

    8.链表写炸了

    $cdots$

    一点经验:

    1.必要的地方要有注释,不然到最后都不清楚自己写的什么。

    2.各种操作建议分成多个函数,便于debug。

    ---------------------------------------------------------------

    总之,打这样的大模拟对自己码力的提升也是有很大帮助的。希望后面的同学,鉴之,戒之。

    代码:

    //一定要注意什么算是类反猪
    //分清id和pid 
    #include<bits/stdc++.h>
    using namespace std;
    int n,m,L,len;
    char s[2010];//牌堆 
    struct pig
    {
        int HP,id,pid,nt,pr,cnt;//血量,表明的身份,给的身份,上一个人,下一个人,计数器 
        bool equip;//诸葛连弩 
        char s[2010];//手牌 
        int next[2010],pre[2010],end;//上一张牌,下一张牌,结尾 
    }a[15];//猪猪 
    int pig[3];//各种身份的数量 
    inline void ins(int x,char c){
        int p=++a[x].cnt;
        a[x].next[a[x].end]=p;//改变原来的顺序 
        a[x].pre[p]=a[x].end;
        a[x].end=p;
        a[x].s[p]=c;
    } 
    inline char get(){if (L<=m) return s[L++];return s[m];}//获得牌 
    inline void start(int x){ins(x,get());ins(x,get());}//摸两张牌 
    inline void del(int x,int pos){//弃牌 
        a[x].next[a[x].pre[pos]]=a[x].next[pos];
        a[x].pre[a[x].next[pos]]=a[x].pre[pos];
        if (a[x].end==pos) a[x].end=a[x].pre[pos];
    } 
    inline void clear(){//清空所有牌 
        a[1].next[0]=0;
        a[1].end=0;
        a[1].equip=0;
    }
    inline void end(){
        if (!pig[0]) {printf("FP
    ");}
        else if (!pig[2]) {printf("MP
    ");}
        for (int i=1;i<=n;i++)
        {
            if (a[i].HP<=0)printf("DEAD
    ");
            else{
                for (int j=a[i].next[0];j;j=a[i].next[j]) printf("%c ",a[i].s[j]);
                printf("
    ");
            }
        }
        exit(0);
    }
    inline int f(int x,int y)//x-->y献殷勤/表敌意。0表敌意,1献殷勤 
    {
        if (a[y].id==-1) return -1;
        if (a[y].id==3){if (a[x].pid==0) return 0;else return -1;}
        if (a[x].pid==0){if (a[y].id==0||a[y].id==1) return 1;else return 0;}
        if (a[x].pid==1){if (a[y].id==1||a[y].id==0) return 1;else return 0;}
        if (a[x].pid==2){if (a[y].id==1||a[y].id==0) return 0;else return 1;}
        return -1;
    }
    inline void isend(){
        if (pig[0]&&pig[2]) return;
        end();
    }//判断游戏是否结束 
    inline void jl(int x){ins(x,get());ins(x,get());ins(x,get());}
    inline void pd(int x,int y){//y是伤害来源,x阵亡 
        if (a[x].pid==2) jl(y);
        if (a[x].pid==1&&a[y].pid==0) clear();
    }
    inline void P(int x,int pos){
        if (a[x].HP==4)return;
        a[x].HP++;
        del(x,pos);
    }//
    inline void isdead(int x,int y){//x是被害人,y是伤害来源 
        if (a[x].HP>0) return;
        for (int i=a[x].next[0];i;i=a[x].next[i]){
            if (a[x].s[i]=='P') P(x,i);
            if (a[x].HP>0) break;
        }
        if (a[x].HP<=0) {
            a[a[x].nt].pr=a[x].pr;a[a[x].pr].nt=a[x].nt;
            pig[a[x].pid]--;
            isend();
            pd(x,y);
        }
    }
    inline void update(int x,int y,int v){
        if (a[x].id>=0&&a[x].id<=2) return;
        if (v==1) {
            if (a[y].pid==0) a[x].id=3;
            return;
        }
        a[x].id=a[x].pid;
    }//只有南蛮入侵和万箭齐发对主公造成伤害时才被认为是类反猪 
    inline void attack(int x,int y){a[y].HP--;isdead(y,x);}//x对y造成伤害
    inline bool have(int x,char c){
        for (int i=a[x].next[0];i;i=a[x].next[i]){
            if (a[x].s[i]==c){
                del(x,i);
                return 1;
            }
        }
        return 0;
    } 
    inline bool K(int x,int pos)//
    {
        int y=a[x].nt;
        int p=f(x,y);
        if (p==-1) return 0;
        if (p==0){
            del(x,pos);
            update(x,y,0);
            if (!have(y,'D'))attack(x,y);
            return 1;
        }
        return 0;
    }
    inline bool J(int x,int y,int v){//无懈可击 
        if (f(x,y)==v&&have(x,'J')){
            update(x,y,0);
            if (!J(x,y,v^1)) return 1;
        }
        for (int i=a[x].nt;i!=x;i=a[i].nt){
            if (f(i,y)==v&&have(i,'J')){
                update(i,y,0);
                if (!J(i,y,v^1)) return 1;
            }
        }
        return 0;
    }
    inline void F(int x,int y)//x对y用决斗,y先出杀 
    {
        if (J(x,y,1)) return;
        while(1){
            swap(x,y);
            if ((a[x].pid==1&&a[y].pid==0)||(!have(x,'K'))) {
                attack(y,x);
                return;
            } 
        }
    }
    inline void nmrq(int x,int y)
    {
        if (!J(x,y,1)&&!have(y,'K')){
            attack(x,y);
            update(x,y,1);
        }
    } 
    inline void wjqf(int x,int y)
    {
        if (!J(x,y,1)&&!have(y,'D')){
            attack(x,y);
            update(x,y,1); 
        }
    }
    inline void work(int x)
    {
        start(x);int flag=0;
        for (int i=a[x].next[0];i;i=a[x].next[i]){
            if (a[x].s[i]=='P') P(x,i);
            if (a[x].s[i]=='K'&&(!flag||a[x].equip)) if (K(x,i)){
                flag++;
                i=0;
            }
            if (a[x].s[i]=='F'){
                if (a[x].pid==2){
                    del(x,i);
                    update(x,1,0);
                    F(x,1);i=0;
                }
                else for(int j=a[x].nt;j!=x;j=a[j].nt){
                    int p=f(x,j);
                    if(p==0){del(x,i);update(x,j,0);F(x,j);i=0;break;}
                }
                if(a[x].HP<=0)return;
            }
            if (a[x].s[i]=='N'){
                del(x,i);
                for (int j=a[x].nt;j!=x;j=a[j].nt) nmrq(x,j);
                i=0;
            }
            if (a[x].s[i]=='W'){
                del(x,i);
                for (int j=a[x].nt;j!=x;j=a[j].nt) wjqf(x,j);
                i=0;
            }
            if (a[x].s[i]=='Z'){
                del(x,i);
                a[x].equip=1;
                i=0;
            }
        }
    }
    inline void solve()
    {
        scanf("%d%d",&n,&m);//n个猪,m张牌
        char c[6];int tot;
        for (int i=1;i<=n;i++)
        {
            scanf("%s",c+1);tot=0;
            if (c[1]=='M') a[i].pid=0;
            else if (c[1]=='Z') a[i].pid=1;
            else a[i].pid=2;
            pig[a[i].pid]++;
            while(++tot<=4) scanf("%s",c+1),ins(i,c[1]);    
        } 
        for (int i=1;i<=m;i++) scanf("%s",c+1),s[i]=c[1];
        for (int i=1;i<=n;i++)
        {
            a[i].nt=(i==n)?1:i+1;
            a[i].pr=(i==1)?n:i-1;
            a[i].id=-1;
            a[i].HP=4; 
        } 
        L=1;a[1].id=0;isend();
        for (int i=1;;i=a[i].nt) work(i);
    }
    int main()
    {
        solve();
        return 0;
    }
  • 相关阅读:
    git(1)-git关联GitHub-windows-转载
    jenkins(4)-jenkins配置邮件通知
    jenkins(3)-linux下安装jenkins(yum install方式)
    【PAT甲级】1090 Highest Price in Supply Chain (25 分)(DFS)
    【PAT甲级】1087 All Roads Lead to Rome (30 分)(MAP【int,string】,邻接表,DFS,模拟,SPFA)
    【PAT甲级】1018 Public Bike Management (30 分)(DFS,SPFA)
    Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
    Atcoder Grand Contest 032C(欧拉回路,DFS判环)
    Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)
    Atcoder Grand Contest 031C(构造,思维,异或,DFS)
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13284249.html
Copyright © 2011-2022 走看看