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;
    }
  • 相关阅读:
    苹果开发者账号证书、授权配置文件设置流程
    JQuery 总结(1) 选择器的使用
    PC和移动端自动识别
    前端获取ip的接口
    AJAX 跨域解决办法
    linux
    复制剪切板实现
    css animation 制作打开动画效果
    弹出层播放视频
    盛世龙图项目总结
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13284249.html
Copyright © 2011-2022 走看看