Luogu P2482 [SDOI2010]猪国杀

Pig Country Kill

很古怪的翻译,不过它确实叫猪(Pig)国(Country)杀(Kill)。

我们来好好整理一下这道题目。题面虽较长,但内容基本清晰,只是有部分很Pig的操作部分,很容易让第一次看见这道题目的人百思不得其解。

先整理一下这道长长的题面。


First:人物

四位玩家,初始四张手牌,血量上限\(4\),初始血量\(4\),会告诉你整个牌堆的牌,每位玩家每个回合从牌堆顶部抽走两张牌,放在自己手牌的右侧。人物分主猪,忠猪,和反猪,主猪只有一只,反猪和忠猪可以有多只,反猪全死主猪获胜,主猪死亡反猪获胜


Second:关于出牌

从主猪开始逆时针旋转,就是沿序号为\(1\) \(2\) \(3\) \(...\) \(n\)的顺序依次出牌。每头猪在出牌的时候都会从左往右挨个判断每张牌是否可用,将可用的牌直接用掉。牌分为\(P\)(桃),\(K\)(杀),\(D\)(闪)和锦囊牌\(F\)(决斗),\(N\)(南猪入侵),\(W\)(万箭齐发),\(J\)(无懈可击)和装备牌\(Z\)(猪哥连弩)。具体操作会在下面贴代码的地方说明。


Third:猪的特性

有南猪入侵和万箭齐发一定用,有桃且生命值没满一定吃,有猪哥连弩一定装,受到杀,南猪入侵,万箭齐发可以用闪,杀,闪抵消一定抵消。


Forth:高级操作

跳忠和跳反,即表明自己是忠猪或是反猪,主猪开局直接跳,忠猪只会跳忠,反猪只会跳反。已经跳(后面我们将跳忠和跳反统称为跳,因为忠猪不会跳反,反猪不会跳忠)了的猪只会对同样跳了的同类献殷勤,同时对跳了的敌猪表敌意。跳反猪同时会对主猪表敌意,因为主猪和忠猪是统一阵营。


Fifth:献殷勤和表敌意

猪A对猪B使用了杀或是决斗,叫作猪\(A\)对猪\(B\)表敌意。猪\(A\)用无懈可击帮猪\(B\)抵挡住攻击性锦囊牌,叫作猪\(A\)对猪\(B\)献殷勤。猪\(C\)对猪\(B\)表敌意,被猪\(A\)用无懈可击抵挡住,叫作猪\(A\)对猪\(B\)先殷勤。猪\(C\)对猪\(B\)献殷勤,被猪\(A\)用无懈可击抵消,叫作猪\(A\)对猪\(B\)表敌意。


Sixth:某些猪的特性

主猪会竭力保护自己,会对跳忠猪献殷勤,对跳反猪表敌意。有“类反猪”一概念,所有对他产生伤害的猪都是“类反猪”(才有了样例中的美好结局),主猪同时会对类反猪表敌意;对忠猪来说,它会对跳反猪(没有类反猪)表敌意,角斗时如果对方是主猪,那么不会出杀,会自愿扣血,即使自己会死,会对主猪和已经跳忠的猪献殷勤;对反猪来说,如果能杀到主猪,一定对主猪表敌意(决斗一定打主猪),如果杀不到,对跳忠猪表敌意,对跳反猪一定献殷勤。

上面的情况如果可以同时有多个对象可以表敌意或献殷勤,选取逆时针找到的第一位。且如果能献殷勤或表敌意,一定做


Seventh:其他

如果主猪死亡,游戏结束。如果主猪杀死了忠猪,主猪所有的手牌(包括装备)全部弃置。如果反猪全部死亡,游戏结束。反猪死亡后,杀他的猪从牌堆处摸三张牌(即使他是反猪)。如果游戏结束,那么游戏直接在此时停止,接下来需要做的所有事情都被截止。杀只能打到自己正后面的那一头猪,决斗可以随便决斗。万箭齐发和南猪入侵对除自己以外的所有人顺时针使用。无懈可击可以对任何人使用,无懈可击可以使无懈可击无效,可以使决斗报废(不使任何一方受到伤害)。大部分手牌无法在非自己的回合使用,在自己濒死时(血量为\(0\))可以使用桃(自己的),在自己的献殷勤对象被表敌意时可以出无懈可击,在自己的表敌意对象被献殷勤时可以出无懈可击。注意有的猪死去后会使一些猪之间的距离产生变化

游戏内容差不多就是这样了,如果看不懂,多看几遍也能理解,如果还是难以理解的话,可以往下看本人的AC代码以及解释,可以帮助你加深对本题的理解。


代码

定义

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
//头文件 

using std::ios;
//用using namespace就不用这个了 

//以下定义虽复杂,但是方便在程序中梳理思路,让我们不易混淆true和false的关系
#define CANNOT_USE_THIS_CARD      true           //不能使用这张牌
#define CAN_USE_THIS_CARD         false          //可以使用这张牌
#define THIS_PIG_IS_DEAD          true           //这头猪死了
#define THIS_PIG_IS_NOT_DEAD      false          //这头猪还没死
#define THIS_PIG_IS_JUMP          true           //这头猪已经跳了
#define THIS_PIG_IS_NOT_JUMP      false          //这头猪还没有跳
#define THIS_PIG_LIKE_BAD         true           //这头猪类反
#define THIS_PIG_DO_NOT_LIKE_BAD  false          //这头猪不类反
#define CROSSBOW_ON               true           //装备猪哥连弩
#define CROSSBOW_DOWN             false          //没有装备猪哥连弩
#define THIS_TURN_USED_KILL       true           //这一回合已经使用杀
#define THIS_TURN_DO_NOT_USE_KILL false          //这一回合还没有使用过杀

struct card                                          //对每一张卡牌制造的结构体
{
    char                    which                   ;//牌的种类
    bool                    use                     ;//牌是否已用
};

typedef std::vector<card>::iterator myit;            //这是牌堆的指针类型 

class pig                                            //对每头猪使用的类
{
private:                                             //私有
    int                     hp                      ;//生命值
    bool                    use_kill                ;//这个回合
    bool                    crossbow                ;//是否有装猪哥连弩
    bool                    jumped                  ;//是否已跳
    bool                    like_bad                ;//是否类反
    void                    die         (int)       ;//死掉了
    void                    clear       (void)      ;//清空
    void                    del         (myit&)     ;//删除某迭代器指向的玩意
    myit                    find        (char)      ;//找某张牌
    void                    cut         (void)      ;//拿一张牌
    void                    use         (card&)     ;//用掉某张牌
    void                    hurt        (int)       ;//受到伤害
    bool                    canuse      (char)      ;//可以使用这张牌
    int                     find_K      (void)      ;//找杀的对象
    int                     find_F      (void)      ;//找人决斗
    bool                    K_respond   (void)      ;//反应杀
    bool                    N_respond   (void)      ;//反应南猪入侵
    bool                    W_respond   (void)      ;//反应万箭齐发
    bool                    ask_J       (int)       ;//询问是否出无懈可击
    bool                    back_J      (int)       ;//反应无懈可击
public:                                              //公共
                            pig         (void)      ;//析构函数
    int                     num                     ;//序号
    bool                    dead                    ;//死没死
    std::string             name                    ;//身份
    std::vector<card>       cards                   ;//手牌
    void                    myturn      (void)      ;//到我的回合了
    void                    jump        (void)      ;//跳
    void                    K           (int)       ;//杀
    void                    F           (int)       ;//决斗
    void                    N           (void)      ;//南猪入侵
    void                    W           (void)      ;//万箭齐发
};

void gameover(std::string);//游戏结束 

int n,m;//猪头数量和牌堆深度
int FP;//反猪数量
card stack[2005];//牌堆(之前想写栈发现不用那么做)
pig member[15];//每头猪
int top=1;//牌堆指针 

定义完毕,接下来只要对着上面的定义实现每个函数就可以了。

虽然这里的宏定义名称非常长,但是在以下的代码中就可以将难以分辨的\(true\) & \(false\)用易于理解的标识符代替,使下文结构更加清晰,Debug时更加方便


主函数

int main()
{
    ios::sync_with_stdio(false);//流优化
    cin>>n>>m;//输入
    for(register int i=1;i<=n;i++)
    {
        cin>>member[i].name;//输入猪头名字
        member[i].num=i;//猪头序号
        if(member[i].name=="FP") FP++;//反猪数量
        for(register int j=1;j<=4;j++)
        {
            card c;
            cin>>c.which;//每张手牌
            c.use=CAN_USE_THIS_CARD;//可以用
            member[i].cards.push_back(c);//手牌放置
        }
    }
    for(top=1;top<=m;top++)
    {//牌堆
        card c;
        cin>>c.which;//输入牌堆中每一张牌的信息
        c.use=CAN_USE_THIS_CARD;//可以用
        stack[top]=c;//把牌放进牌堆
    }
    top=1;//原来想写个stack,后来废了
    int point=0;//轮到谁了
    member[1].jump();//主猪跳出来
    while(true)
    {
        point=point%n+1;//一个圈
        if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果他死了,跳过
        member[point].myturn();//这是他的回合
    }
}

主函数结构基本清晰,处理了输入,牌堆,手牌,还有回合的问题。

打完了这些,游戏基本格局就以确定,接下来实现游戏内容。


游戏结束处理

void gameover(std::string who)//谁赢了
{//死掉了
    cout<<who<<std::endl;//输出谁赢了
    for(register int i=1;i<=n;i++)
    {
        if(member[i].dead==THIS_PIG_IS_DEAD) cout<<"DEAD";//如果他死了
        else for(myit it=member[i].cards.begin();it!=member[i].cards.end();it++)
        {//输出他的牌
            if(it->use==CANNOT_USE_THIS_CARD) continue;//如果这张牌已经使用过,那么不存在于牌堆
            cout<<it->which<<' ';//输出手牌的信息,然后打个空格
        }
        cout<<std::endl;//换行
    }
    exit(0);//结束这个程序
}

死亡处理

void pig::die(int who)
{//死了
    this->dead=THIS_PIG_IS_DEAD;//这玩意死了
    if(this->name=="MP")//如果主猪死了
    {
        gameover("FP");//反猪赢了
    }
    if(this->name=="FP") FP--;//如果反猪死了,总数减减
    if(FP==0) gameover("MP");//如果反猪死光了
    if(this->name=="FP")//如果杀死了是反猪
    {
        member[who].cut();//拿牌x1
        member[who].cut();//拿牌x2
        member[who].cut();//拿牌x3
    }
    if(this->name=="ZP"&&member[who].name=="MP")
    {//如果主猪杀掉了忠猪
        member[who].clear();//清空
    }
    return ;
}

手牌的清空、删除、寻找和拿牌等

void pig::clear(void)
{
    this->cards.clear();//自己的手牌清空
    this->crossbow=CROSSBOW_DOWN;//把弩卸掉
    return ;
}

void pig::del(myit& it)
{//删除某张牌
    it->use=CANNOT_USE_THIS_CARD;//删除这个迭代器指向的牌,让它不可用
    return ;
}

myit pig::find(char which)
{//寻找某张牌
    for(myit it=this->cards.begin();it!=this->cards.end();it++)
    {//搜索每一张牌
        if(it->use==CANNOT_USE_THIS_CARD) continue;//如果用过了
        if(it->which==which) return it;//如果这是我要找的牌
    }
    return this->cards.end();//找不到,输出vector的end
}

void pig::cut(void)
{
    this->cards.push_back(stack[top++]);//把牌堆上面的拿掉
    if(top>m) top=m;//好像有数据说牌不够
    return;
}

void pig::use(card& c)
{//用某张牌
    if(c.which=='K')
    {//如果这是杀
        int who;
        who=this->find_K();//找一个人来杀
        if(who!=0)//如果找到了
        {
            c.use=CANNOT_USE_THIS_CARD;//用掉
            this->K(who);//杀它
        }
    }
    else if(c.which=='P')
    {//如果这是桃
        if(this->hp<4)//如果它的生命值没有满
        {
            c.use=CANNOT_USE_THIS_CARD;//用掉
            this->hp++;//生命值加一点
        }
    }
    else if(c.which=='F')
    {//如果这是决斗
        int who;
        who=this->find_F();//找个人来决斗
        if(who!=0)//如果找到了
        {
            c.use=CANNOT_USE_THIS_CARD;//用掉
            this->F(who);//决斗他
        }
    }
    else if(c.which=='N')
    {//南猪入侵
        c.use=CANNOT_USE_THIS_CARD;//用掉
        this->N();//使用南猪入侵
    }
    else if(c.which=='W')
    {//万箭齐发
        c.use=CANNOT_USE_THIS_CARD;//用掉
        this->W();//使用万箭齐发
    }
    else if(c.which=='Z')
    {//猪哥连弩
        c.use=CANNOT_USE_THIS_CARD;//用掉
        this->crossbow=CROSSBOW_ON;//装上
    }
}

void pig::hurt(int who)//谁杀的
{
    this->hp--;//受到伤害
    if(this->hp==0)//如果他死了
    {
        myit t=this->find('P');//找桃
        if(t==this->cards.end()) this->die(who);//找不到,死掉
        else
        {
            this->del(t);//找到了,用掉
            this->hp++;//生命值加一点
        }
    }
}

bool pig::canuse(char c)
{//判断能否使用
    if(c=='P'&&this->hp<4) return true;//如果是桃子且生命值未满
    if((c=='K'&&this->find_K()!=0)||(c=='F'&&this->find_F()!=0)||c=='N'||c=='W'||c=='Z') return true;//如果可以用
    return false;//不然就不该用
}

以上内容理解起来都较为简单,下面的代码难度会有所提高

寻找‘杀‘的目标

对于主猪、忠猪和反猪来说,他们的目的不同,但杀的距离都相同,为\(1\),对于每头猪,我们找到他后方的第一头猪(也是它唯一能杀到的一头猪),判断是否应该对它用杀,如果该用,返回这头猪的编号;如果不该用,直接结束函数,返回\(0\)(表示没有找到杀的猪)。

主猪要找的是跳反猪和类反猪,忠猪要找的是跳反猪,反猪要找的是跳忠猪或是主猪。

int pig::find_K(void)
{//找个人来杀
    if(this->use_kill==THIS_TURN_USED_KILL&&this->crossbow==CROSSBOW_DOWN) return 0;//如果这回合已经用过了杀而没有装过猪哥连弩
    if(this->name=="MP")
    {//我是主猪
        for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
        {//逆时针寻找
            if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果死掉了,跳过去
            if(member[point].like_bad==THIS_PIG_LIKE_BAD||(member[point].name=="FP"&&member[point].jumped==THIS_PIG_IS_JUMP))
            {//如果这猪是类反猪或是已经跳反
                return point;//就决斗他
            }//否则继续找
            else return 0;
        }
    }
    else if(this->name=="ZP")
    {//我是忠猪
        for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
        {//逆时针寻找
            if(member[point].dead==THIS_PIG_IS_DEAD) continue;//这玩意死了,跳过去
            if(member[point].name=="FP"&&member[point].jumped==THIS_PIG_IS_JUMP)
            {//如果这是已经跳反的猪头
                return point;//就杀他
            }
            else return 0;
        }
    }
    else if(this->name=="FP")
    {//我是反猪
        for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
        {//逆时针寻找
            if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果死掉了,先过去
            if(member[point].name!="FP"&&member[point].jumped==THIS_PIG_IS_JUMP)//找到第一头猪,如果它跳忠或是是主猪
            {
                return point;//杀他
            }
            else
            {
                return 0;//杀不到
            }
        }
    }
    return 0;
}

寻找‘决斗‘的目标

与‘杀‘类似,对于主猪、忠猪和反猪来说,他们寻找决斗目标的目的也不同,但考虑到决斗牌无距离限制,所以我们可以直接攻击游戏上存活的任何一头猪。

主猪杀的是逆时针下去的第一头跳反猪或类反猪,忠猪杀的是逆时针下去的第一头跳反猪,反猪直接杀主猪

int pig::find_F(void)
{//找一个人来决斗
    if(this->name=="MP")
    {//我是主猪
        for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
        {//逆时针寻找
            if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果死掉了,跳过去
            if(member[point].like_bad==THIS_PIG_LIKE_BAD||(member[point].name=="FP"&&member[point].jumped==THIS_PIG_IS_JUMP))
            {//如果这猪是类反猪或是已经跳反
                return point;//就决斗他
            }//否则继续找
        }
    }
    else if(this->name=="ZP")
    {//我是忠猪
        for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
        {//逆时针寻找
            if(member[point].dead==THIS_PIG_IS_DEAD) continue;//这玩意死了,跳过去
            if(member[point].name=="FP"&&member[point].jumped==THIS_PIG_IS_JUMP)
            {//如果这是已经跳反的猪头
                return point;//就决斗他
            }
        }
    }
    else if(this->name=="FP")
    {//我是反猪
        return 1;//杀主猪
    }
    return 0;
}

攻击的反应函数

任何被使用杀、南猪入侵、万箭齐发的猪都会在背后运行这些函数。

杀的反应函数寻找手牌中是否有闪,南猪入侵的反应函数寻找手牌中是否有杀,万箭齐发的反应函数寻找手牌中是否有杀。

bool pig::K_respond(void)
{//杀的反应
    myit it=this->find('D');//找一找有没有闪
    if(it==this->cards.end()) return false;//没有闪,反应无效
    this->del(it);//删掉闪
    return true;//有效反应
}

bool pig::N_respond(void)
{//回应南猪入侵
    myit it=this->find('K');//找一找杀
    if(it==this->cards.end()) return false;//找不到
    this->del(it);//删掉它
    return true;
}

bool pig::W_respond(void)
{//回应万箭齐发
    myit it=this->find('D');//找闪
    if(it==this->cards.end()) return false;//没找到
    this->del(it);//用掉它
    return true;
}

接下来重点中的重点了

无懈可击

这里我们写两个递归函数,先看我们第一个函数,这个函数是被攻击性锦囊攻击的人调用的函数,形参表明谁伤害的自己,寻找逆时针(注意从自己开始,不是从自己后面的那一位开始,因为自己如果跳了也是会保护自己的)找到的第一头有无懈可击的同类,让它来帮你出无懈可击。

如果这头猪还没有跳是不可以寻求帮助的

bool pig::ask_J(int who)//找人给我挡牌,who对我发起攻击
{//询问无懈可击
    bool check=true;
    if(this->jumped==THIS_PIG_IS_NOT_JUMP) return false;//如果没有跳,那就不用
    for(register int point=who;point!=who||check==true;point=point%n+1)
    {//逆时针查询
        if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果死掉了
        check=false;
        if(this->name!="FP"&&member[point].name=="FP") continue;//如果有敌意,不用
        if(this->name=="FP"&&member[point].name!="FP") continue;//如果要打上,不用
        myit it=member[point].find('J');//找找无懈可击
        if(it!=member[point].cards.end())
        {
            member[point].del(it);//删掉这张牌
            member[point].jump();//跳
            return !member[point].back_J(this->num);//对抗一下
        }
    }
    return false;
}

考虑到上方的献殷勤可能会被表敌意给无效掉,所以才有了下面这个函数。调用过上面的函数的猪都要让帮他出牌的那头猪继续做下面的工作

内容:找到逆时针的第一头有无懈可击的敌猪,让它对自己的无懈可击出无懈可击。

其实你测过几个例子就会发现,每当主猪受到伤害后,忠猪和反猪两阵营一定有一方一张无懈可击都没有了,这是为什么?

真的是Pig思维啊

bool pig::back_J(int to)
{//无懈可击的对抗
    for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
    {//逆时针扫描
        if(member[point].dead==THIS_PIG_IS_DEAD) continue;//已逝者先行
        if(this->name!="FP"&&member[point].name=="ZP") continue;//如果同类,不打
        if(this->name=="FP"&&member[point].name=="FP") continue;//如果同类,不打
        if(this->name=="ZP"&&member[point].name!="FP") continue;//如果友好,不打
        myit it=member[point].find('J');//找一张无懈可击
        if(it!=member[point].cards.end())
        {
            member[point].del(it);//用它
            member[point].jump();//同时自己得跳
            return !member[to].ask_J(point);//再回击一下(很骚啊)
        }
    }
    return false;
}


补一个构造函数

pig::pig(void)
{
    this->hp=4;//生命值
    this->dead=THIS_PIG_IS_NOT_DEAD;//没死
    this->jumped=THIS_PIG_IS_NOT_JUMP;//有没有跳
    this->crossbow=CROSSBOW_DOWN;//有没有装弩
    this->like_bad=THIS_PIG_DO_NOT_LIKE_BAD;//是否类反
    this->use_kill=THIS_TURN_DO_NOT_USE_KILL;//这回合没有使用过杀
}


再补一个回合函数

基于本人在最上面\(class\)里面写的独特的从\(private\)到\(public\)的顺序,加上本人的强迫症(必须按照声明的顺序实现函数),所以下方的函数都比较 *

这个回合函数很模拟,按照题意,摸两张牌,从左到右扫一遍手牌,对每张牌来个判断,能通过判断的牌用掉。

注意用掉一张牌后需要从头开始再扫一遍手牌,这是为什么?

如果手牌是KKKKZKK,那么你就会知道为什么要这么做了。

void pig::myturn(void)
{
    this->cut();//拿牌x1
    this->cut();//拿牌x2
    bool check=true;//有牌拿
    while(check)
    {//只要不结束
        check=false;//有没有用牌
        for(myit it=this->cards.begin();it!=this->cards.end();it++)
        {//在手牌中从左到右扫一遍
            if(it->use==CANNOT_USE_THIS_CARD) continue;//如果不能用了
            if(canuse(it->which)==false) continue;//判断一下现在能不能用
            use(*it);//用它
            if(this->dead==THIS_PIG_IS_DEAD) return;//如果这玩意死了
            it=this->cards.begin()-1;//从头开始重新扫
            check=true;//用过牌了
        }
    }
    member[this->num].use_kill=THIS_TURN_DO_NOT_USE_KILL;//重置是否用过杀
    return;
}


考虑到忠猪只会跳忠,反猪只会跳反,所以我们只要知道这头猪的身份以及他有没有跳就可以得知它是跳忠还是跳反,所以只需要对每头猪都开一个\(bool\)标识表示这头猪有没有跳。

void pig::jump(void)
{//跳
    this->jumped=THIS_PIG_IS_JUMP;//开启标识
    return ;
}


嗯,接下来的一些操作也比较重要 ,反正没有无懈可击难就对了

考虑到杀必然会使出杀者跳。因为杀主猪的猪一定是反猪,杀忠猪的猪也是跳反猪,杀反猪的猪一定是忠猪或是主猪。所以只要你用了杀,你就跳了。

其次,跳了之后你就可以取消类反标识了(即使你跳反了,取消了标识依旧被打)。

然后,就没有什么花头了,杀的人在前面函数中已经选定,照着打就可以了。

void pig::K(int to)
{//杀
    if(this->use_kill==THIS_TURN_USED_KILL&&this->crossbow==false) return;//如果这回合使用过杀而没有使用猪哥连弩
    this->jump();//必然跳
    this->like_bad=THIS_PIG_DO_NOT_LIKE_BAD;//取消类反(即使成为跳反猪)
    this->use_kill=THIS_TURN_USED_KILL;//用过杀了
    if(member[to].K_respond()==false)
    {//如果对方没有对杀产生有效反应
        member[to].hurt(this->num);//伤它一滴血
    }
}


决斗

与杀相似,只要你用了决斗你就必然跳,注意如果忠猪被主猪使用无懈可击,它会直接扛上一点伤害,然后结束决斗

此外还需注意记录到底是谁出的最后一张杀,决定了谁受到另一方的伤害。

void pig::F(int to)
{//决斗
#ifdef DEBUG
    printf("%d对%d使用了决斗\n",this->num,to);
#endif
    this->jump();//先跳再说
    this->like_bad=THIS_PIG_DO_NOT_LIKE_BAD;//类反猪取消
    if(member[to].ask_J(this->num)==true) return;//询问无懈可击,如果使用,跳过
    if(this->name=="MP"&&member[to].name=="ZP")
    {//如果主猪对忠猪用
        member[to].hurt(this->num);//忠猪受伤
        return;
    }
    int who;//判定谁提供伤害
    while(true)
    {
        myit t;
        t=member[to].find('K');//找杀
        if(t==member[to].cards.end())//没找到
        {
            who=2;//我对你产生伤害
            break;
        }
        else member[to].del(t); //用掉这张牌
        t=this->find('K');//从我这里找杀
        if(t==this->cards.end())
        {
            who=1;//你对我产生伤害
            break;
        }
        else this->del(t);//删掉这张牌
    }
    if(who==2)
    {//你伤了我
        member[to].hurt(this->num);//伤害函数
    }
    if(who==1)
    {//我伤了你
        this->hurt(to);//伤害函数
    }
}


南猪入侵

然后就是南猪入侵了。从出牌者的下一位开始,逆时针旋转一周,对每个人进行无懈可击判定和南猪入侵回应判定。

void pig::N(void)
{//南猪入侵
#ifdef DEBUG
    printf("%d使用了南猪入侵\n",this->num);
#endif
    for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
    {//逆时针查询
        if(member[point].dead==THIS_PIG_IS_DEAD) continue;//死掉了,跳过去
        if(member[point].ask_J(this->num)==true) continue;//看看有没有无懈可击
        if(member[point].N_respond()==false)
        {//判断是否反应
            member[point].hurt(this->num);//受到伤害
            if(member[point].name=="MP"&&this->jumped==THIS_PIG_IS_NOT_JUMP)
            {//如果主猪受到了攻击且该猪没有跳
                this->like_bad=THIS_PIG_LIKE_BAD;//定义为类反猪
            }
        }
    }
}


万箭齐发

跟南猪入侵大体相同,就是改了几个字。

void pig::W(void)
{//万箭齐发
#ifdef DEBUG
    printf("%d使用了万箭齐发\n",this->num);
#endif
    for(register int point=this->num%n+1;point!=this->num;point=point%n+1)
    {//逆时针搜索
        if(member[point].dead==THIS_PIG_IS_DEAD) continue;//如果死了,跳过
        if(member[point].ask_J(this->num)==true) continue;//询问是否有无懈可击
        if(member[point].W_respond()==false)//回应万箭齐发
        {
            member[point].hurt(this->num);//受到伤害
            if(member[point].name=="MP"&&this->jumped==THIS_PIG_IS_NOT_JUMP)
            {//主猪受到攻击,这头猪没有跳
                this->like_bad=THIS_PIG_LIKE_BAD;//定义为类反猪
            }
        }
    }
}

本题完整代码



转载自本人Luogu Blog这篇文章

原文地址:https://www.cnblogs.com/macesuted/p/solution-P2482.html

时间: 2024-11-07 20:14:32

Luogu P2482 [SDOI2010]猪国杀的相关文章

洛谷P2482 [SDOI2010]猪国杀

题目:https://www.luogu.org/problemnew/show/P2482 题目描述 <猪国杀>是一种多猪牌类回合制游戏,一共有三种角色:主猪,忠猪,反猪.每局游戏主猪有且只有一只,忠猪和反猪可以有多只,每只猪扮演一种角色. 游戏目的: 主猪(MP):自己存活的情况下消灭所有的反猪. 忠猪(ZP):不惜一切保护主猪,胜利条件与主猪相同. 反猪(AP):杀死主猪. 游戏过程: 游戏开始时候,每个玩家手里都会有4张牌,且体力上限和初始体力都是4. 开始游戏时,从主猪开始,按照逆时

[SDOI2010]猪国杀

一个能看的题面 : https://mubu.com/doc/2707815814591da4 晚上闲的没事干被Refun忽悠着写猪国杀玩 然后一下子写到现在 浪费了一晚上+一上午 这题一堆肥肠SB的规则总之就是让你用他们的智商玩猪国杀 这是一篇没有任何意义的题解 附上10K的代码 然后那个忽悠我的人一直在嘲讽我写的长TAT #include<map> #include<cstdio> #include<vector> #include<cstring> #

Luogu2482 [SDOI2010]猪国杀

题意 ...... https://www.luogu.org/problemnew/show/P2482 总结 题解好像没什么好写的 一些经验吧...... 提前分配好一些比较好的变量名 建议先声明函数再定义函数 打开Dev-C++的代码结构或者自己在草稿纸上写结构 每次调试之前自己浏览一遍代码并手推一遍样例,特别是修改了之后注意自己修改的地方,不要编译了历史版本 不要写的十分复杂,能简单写简单写 善用assert 善用注释 1 //Created By Creeper_LKF 2 //Cau

bzoj1972: [Sdoi2010]猪国杀 模拟

模拟.认真读题,理清思路. #include<cstdio> #include<list> #include<cstdlib> const int N=10; #define FOR(a,k)for(A k=P[a].begin();k!=P[a].end();++k) using namespace std; int n,m; int HP[N],ST[N]; bool ID[N],Z[N]; list<char> P[N]; typedef list&l

洛谷P2482 猪国杀

题目描述 <猪国杀>是一种多猪牌类回合制游戏,一共有三种角色:主猪,忠猪,反猪.每局游戏主猪有且只有一只,忠猪和反猪可以有多只,每只猪扮演一种角色. 游戏目的: 主猪(MP):自己存活的情况下消灭所有的反猪. 忠猪(ZP):不惜一切保护主猪,胜利条件与主猪相同. 反猪(AP):杀死主猪. 游戏过程: 游戏开始时候,每个玩家手里都会有4张牌,且体力上限和初始体力都是4. 开始游戏时,从主猪开始,按照逆时针方向(数据中就是按照编号从1,2,3..n,1..的顺序)依次行动. 每个玩家自己的回合可以

Yali 19 - 8 - 6 test T2 猪国(pig) 题解

T2 猪国 题?描述 猪国是?个由 \(n\) 个城市组成的国家. 国王意识到了"要致富,先修路"这句话的重要性,它决定?规模修路.不巧的是,猪国的 猪们不太会?程,于是只能请隔壁鸡国的鸡建狂魔来帮忙修路.鸡建狂魔看不起猪,于是随 便建设了 \(m\) 条单向的路.尽管如此,每条路还是产?了或多或少的价值. 路修好了,经济却上不来.国王经过调研,发现了道路的巨?缺陷.具体来说,猪?们?向 感不好,?旦存在若?条路能组成?个环,那么可怜的猪?就有可能在环??绕来绕去,这 样甚?会产?反效

luogu P2480 [SDOI2010]古代猪文

M_sea:这道题你分析完后就是一堆板子 废话 理解完题意后,我们要求的东西是\(G^s(s=\sum_{d|n} \binom{n}{d})\) 但是这个指数\(s\)算出来非常大,,, 我们可以利用费马小定理 \(a^{(p-1)}\equiv1(mod\ p)(gcd(a,p)=1)\) 由此我们可以得到\(G^s \equiv G^{s\ mod\ (p-1)}(mod\ p)\) 组合数部分可以使用\(Lucas\)定理求解 但是,本题的\(mod-1\)不是一个质数,它可以质因数分解

Luogu P2480 [SDOI2010]古代猪文 卢卡斯+组合+CRT

好吧刚开始以为扩展卢卡斯然后就往上套..结果奇奇怪怪又WA又T...后来才意识到它的因子都是质数...qwq怕不是这就是学知识学傻了.. 题意:$ G^{\Sigma_{d|n} \space C_n^d}\space mod \space 999911659$ 首先发现999911659是个质数,所以根据欧拉定理的推论有 $ G^{\Sigma_{d|n}\space C_n^d} \equiv G^{\Sigma_{d|n}\space C_n^d\space mod \space\phi(

luogu P2481 [SDOI2010]代码拍卖会

luogu 题目中的那个大数一定是若干个1+若干个2+若干个3...+若干个9组成的,显然可以转化成9个\(\underbrace {111...1}_{a_i个1}(0\le a_1\le a_2\le a_3...\le a_9,a_9=n)\)之和 然后模数只有500,所以可以考虑处理出所有\(\mod p =i\)的不同长度的\(111...1\)个数记为\(cnt_i\),考虑dp求答案,设\(f_{i,j,k}\)表示考虑了前\(i\)个剩余类,用了\(j\)个\(111...1\)