P9966 [THUPC 2024 初赛] 机器人 题解

· · 题解

-1.HACK

HACK1

输入 1

2 2 7
1 1
REPLACE 0 1 MOVE 0 1
MIRROR 0 1
0 0
SLACKOFF
REPLACE 0 2 SLACKOFF

输出 1:(着重看最后一行的 left

Robot 0 replaces a line of command of Robot 1.
Robot 0 modifies a line of command of Robot 1.
Robot 1 moves its right hand towards Robot 1.
Robot 1 replaces a line of command of Robot 0.
Robot 0 replaces a line of command of Robot 1.
Robot 0 slacks off.
Robot 1 moves its left hand towards Robot 1.

错于此,转到 坑点1

HACK2

输入 2

2 2 4
1 1
MOVE 0 1
ACTIVATE 0
0 0
SLACKOFF
SLACKOFF

输出 2:(着重看最后一行)

Robot 0 moves its left hand towards Robot 0.
Robot 0 activates Robot 0.
Robot 0 moves its left hand towards Robot 1.
Robot 0 activates Robot 1.

错于此,转到 坑点2

HACK3

感谢用户 codingwen 在这个贴子中发现的问题。

3 3 11
2 2
SWAP 0 2 2
SWAP 1 2 2
SWAP 1 2 1
1 2
MIRROR 0 2
REPLACE 1 2 SLACKOFF
SLACKOFF
0 2
SLACKOFF
ACTIVATE 1
MIRROR 0 3

问题在于原代码中,Replaceupdate 函数中,当恰好更改到当前的指令时,hrbt 会变化,与 坑点2 的错误一致。

修改方法同 坑点2,提前记录下 hrbt 即可。

(还是 Replace 操作修改到自身的概率太低了,导致我都退役了才被叉)

0.前言

你说得对,但是这篇题解很蠢,可能对继承的理解也不是很深,有更简单的写法可以提出来,shaber 作者学习一下。

1.继承

定义一个 struct Command 表示一个指令的基类。可以共用同名函数(需要用虚函数即 virtual),以及同名变量。

代码如下(变量解释见注释):

struct Command
{
    int id;//表示指令的类型,实际上也可以不计这个,直接使用typeid或者其他来实现,id 按照题目指令依次为 1~6。
    bool h,tri;//h:这条指令的 h,tri:这条指令是否是trigger触发的
    inline virtual void init()=0;//这条指令的初始化
    inline virtual void update(int rbt,int cid)=0;//执行这条指令
};

下面以 SLACKOFF 来举个例子:

struct Slackoff:Command //:后接继承的函数
{
    inline void update(int rbt,int cid)override//表示 Command 的虚函数
    {
        printf("Robot %d slacks off.\n",rbt);if(!--k)exit(0);//输出
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);//检查右手的 trigger 条件
    }
    inline void init()override{id=0;tri=0;}//tri=0 不为 trigger 指令,id 为 0
};

一些实现:

dynamic_cast<Trigger*>cmd

可以强制转化 cmdTrigger 类,此时就可以调用 Trigger 专属的函数。

2.指令类

刚刚介绍过了。

3.机器人类

需要存储这个机器人的所有指令,此时可以直接使用 Command* cmd[10]; 来描述这个机器人的指令。

注意,基类不能直接使用 Command 定义,定义时一定要用像 Slackoff 这样的类定义。

此时继承的方便性就呈现出来了,当你要依次激活这个机器人的指令的时候,只需要:

for(int i=0;i<m;i++)cmd[i]->update(...);//内部填的啥待会再说

就行了。

具体的,机器人类目前要存储:

SLACKOFF 刚刚介绍过了,不再阐述。

MOVE 指令要新存一个变量 z,表示和题目相同。

Move 内实现的 init 便需要读入 hz

对于 Move 的 update,我们要找到这个机器人的 h 手,即 Rbt[rbt].hd[h] (为了方便,我们将其定义为 hrbt),然后将其循环移位即可。注意输出要在移位后执行。

4.2 SWAP,MIRROR,REPLACE

这三个指令是对机器人内部的指令进行修改,具体实现如下:

SWAP

十分简单,直接交换两个机器人的 Command 指针即可。

REPLACE

发现这个指令内部要记一个指令指针 Command* comd,表示这个指令替换时替换成的指令,update 也很好实现:只要把目标位置上的指令指针指向 comd 即可。

坑点3

同坑点2,具体的在 HACK3 中叙述过了。

只是作者代码写的太烂了,估计不算什么坑点。

MIRROR

也很简单,只要把目标指令的 h 值异或一下即可。

吗?

坑点1

考虑一组 hack:

2 2 7
1 1
REPLACE 0 1 MOVE 0 1
MIRROR 0 1
0 0
SLACKOFF
REPLACE 0 2 SLACKOFF

按照我们的想法,最后一句输出:Robot 1 moves its right hand towards Robot 1.,实际上应该是 left hand

我们发现一件事:当 MIRROR 指令反转的是 REPLACE 过后的指令时,此时直接更改了 REPLACE 指令内部的 comd。然而我们只想要反转真实的指令:

此时就要在机器人类中记一个 h[10] 表示每个指令的“反转标记”,那么,对于所有 update 函数来说,我们就要传入一个 cid 表示当前指令是 rbt 的第几条指令,然后使用 Rbt[rbt].hd[Rbt[rbt].h[cid]^h] 来表示当前指令的手。

PS:其实可以把指令中的 h 和机器人类中的 h 合并的,但是我懒,于是就直接改了。

再次考虑有无其他可能更改 comd 的指令,发现没有,那么考虑适配:

SWAP:要多交换两条指令的 h 数组中的数字。

REPLACE:要将当前指令的 h 数组置为 0。

MIRROR:只要将指向指令的 h 数组取反即可。

于是,我们便完成了基础指令。

5.高级指令

和普通指令没有区别,也是继承基类 Command

5.1 ACTIVATE

update 中直接执行 for(int i=0;i<m;i++)Rbt[hrbt].cmd[i]->update(hrbt,i); 即可。

吗?

坑点 2

你发现一件事,执行每条指令的 update 时,当前机器人的手可能会动,所以不能直接用 hrbt,而是要在 for 前将指向的机器人存下来。

5.2 TRIGGER

TRIGGER 指令要存储的信息有:type 表示检测的条件,comd 表示执行的指令。

读题,发现 TRIGGER 指令是在每次指令操作结束后都有可能执行,于是对于机器人类定义一个函数,表示查找是否会执行 TRIGGER 指令。代码如下:

inline void check_trigger(Command* comd)
{
    for(int i=0;i<m;i++)if(cmd[i]->id==6)
    if(dynamic_cast<Trigger*>(cmd[i])->type==6)
    {
        if(comd->tri)return dynamic_cast<Trigger*>(cmd[i])->work(rbt,i);
    }
    else if(dynamic_cast<Trigger*>(cmd[i])->type==comd->id)
    return dynamic_cast<Trigger*>(cmd[i])->work(rbt,i);
}

注意到题目中的:其他机器人,所以我们要在所有的 update 后执行:

if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);

其中 dynamic_cast<Trigger*> 为强转为 Trigger 类,且 id 比较也可以用 typeid 来解决。

介于 TRIGGER 并没有实际执行,所以其 update 为空,新开一个 work 函数,作为执行 TRIGGER 时调用的函数。代码:

inline void work(int rbt,int cid){comd->update(rbt,cid);}

至此,我们完成了所有指令。

6.读入指令

定义一个函数 create 表示读入一个指令并且返回一个 Command*

代码容易实现(可能有更好的实现,你说的对,但是):

inline Command* create()
{
    string s;cin>>s;Command *comd;
    if(s=="SLACKOFF")comd=new Slackoff;
    else if(s=="MOVE")comd=new Move;
    else if(s=="SWAP")comd=new Swap;
    else if(s=="MIRROR")comd=new Mirror;
    else if(s=="REPLACE")comd=new Replace;
    else if(s=="ACTIVATE")comd=new Activate;
    else if(s=="TRIGGER")comd=new Trigger;
    comd->init();
    return comd;
}

我们来考虑实现每个指令的 init

6.1 SLACKOFF,SWAP,MIRROR,ACTIVATE

这四种指令的 init 是容易的,直接读入并将 idtri 赋值即可。

6.2 REPLACE

comd=create() 就能完成,剩余部分同上。

6.3 TRIGGER

最前面的 COMMANDNAME 确定 type,剩余部分同上。

至此,所有部分都完成了。

7.完整代码

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x(0),f(1);char c=getchar();
    while(c<'0'||c>'9')f=c=='-'?-1:1,c=getchar();
    while(c<='9'&&c>='0')x=x*10+c-48,c=getchar();
    return x*f;
}
#define hrbt Rbt[rbt].hd[h^Rbt[rbt].h[cid]]
int n,m,k;
struct Command
{
    int id;bool h,tri;
    inline virtual void init()=0;
    inline virtual void update(int rbt,int cid)=0;
};
inline Command* create();
struct Trigger:Command
{
    int type;Command* comd;
    inline void init()override
    {
        string s;cin>>s;
        if(s=="SLACKOFF:")type=0;
        else if(s=="MOVE:")type=1;
        else if(s=="SWAP:")type=2;
        else if(s=="MIRROR:")type=3;
        else if(s=="REPLACE:")type=4;
        else if(s=="ACTIVATE:")type=5;
        else if(s=="TRIGGER:")type=6;
        comd=create();
        id=6;tri=0;comd->tri=1;
    }
    inline void update(int rbt,int cid)override{};inline void work(int rbt,int cid){comd->update(rbt,cid);}
};
struct Robot
{
    int hd[2],rbt;Command* cmd[10];bool h[10];
    inline void check_trigger(Command* comd)
    {
        for(int i=0;i<m;i++)if(cmd[i]->id==6)
        if(dynamic_cast<Trigger*>(cmd[i])->type==6)
        {
            if(comd->tri)return dynamic_cast<Trigger*>(cmd[i])->work(rbt,i);
        }
        else if(dynamic_cast<Trigger*>(cmd[i])->type==comd->id)
        return dynamic_cast<Trigger*>(cmd[i])->work(rbt,i);
    }
}Rbt[110];
struct Slackoff:Command
{
    inline void update(int rbt,int cid)override{printf("Robot %d slacks off.\n",rbt);if(!--k)exit(0);
    if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);}
    inline void init()override{id=0;tri=0;}
};
inline void move(int &x,int y){x=(x+y)%n;}
struct Move:Command
{
    int z;
    inline void init()override{h=read();z=read();id=1;tri=0;}
    inline void update(int rbt,int cid)override
    {
        move(hrbt,z);
        printf("Robot %d moves its %s hand towards Robot %d.\n",rbt,h^Rbt[rbt].h[cid]?"right":"left",hrbt);if(!--k)exit(0);
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);
    }
};
template<typename T>
inline void swaps(T &a,T &b,bool &ta,bool &tb){swap(a,b);swap(ta,tb);}
struct Swap:Command
{
    int x,y;
    inline void init()override{h=read();x=read()-1;y=read()-1;id=2;tri=0;}
    inline void update(int rbt,int cid)override
    {
        printf("Robot %d swaps a line of command with Robot %d.\n",rbt,hrbt);if(!--k)exit(0);
        swaps(Rbt[rbt].cmd[y],Rbt[hrbt].cmd[x],Rbt[rbt].h[y],Rbt[hrbt].h[x]);
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);
    }
};
struct Mirror:Command
{
    int x;
    inline void init()override{h=read();x=read()-1;id=3;tri=0;}
    inline void update(int rbt,int cid)override
    {
        printf("Robot %d modifies a line of command of Robot %d.\n",rbt,hrbt);if(!--k)exit(0);
        Rbt[hrbt].h[x]^=1;
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);
    }
};
struct Replace:Command
{
    int x;Command* comd;
    inline void init()override
    {
        h=read();x=read()-1;
        comd=create();
        id=4;tri=0;
    }
    inline void update(int rbt,int cid)override
    {
        printf("Robot %d replaces a line of command of Robot %d.\n",rbt,hrbt);if(!--k)exit(0);
        // Rbt[hrbt].h[x]=0;Rbt[hrbt].cmd[x]=comd; 感谢 codingwen 发现此行的错误
        int qwq=hrbt;Rbt[qwq].h[x]=0;Rbt[qwq].cmd[x]=comd;
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);
    }
};
struct Activate:Command
{
    inline void init()override{h=read();id=5;tri=0;}
    inline void update(int rbt,int cid)override
    {
        printf("Robot %d activates Robot %d.\n",rbt,hrbt);if(!--k)exit(0);
        int qwq=hrbt;for(int i=0;i<m;i++)Rbt[qwq].cmd[i]->update(qwq,i);
        if(Rbt[rbt].hd[1]!=rbt)Rbt[Rbt[rbt].hd[1]].check_trigger(this);
    }
};
inline Command* create()
{
    string s;cin>>s;Command *comd;
    if(s=="SLACKOFF")comd=new Slackoff;
    else if(s=="MOVE")comd=new Move;
    else if(s=="SWAP")comd=new Swap;
    else if(s=="MIRROR")comd=new Mirror;
    else if(s=="REPLACE")comd=new Replace;
    else if(s=="ACTIVATE")comd=new Activate;
    else if(s=="TRIGGER")comd=new Trigger;
    comd->init();
    return comd;
}
int main()
{
    n=read(),m=read(),k=read();
    for(int i=0;i<n;i++)
    {
        Rbt[i].hd[0]=read(),Rbt[i].hd[1]=read();Rbt[i].rbt=i;
        for(int j=0;j<m;j++)Rbt[i].cmd[j]=create();
    }
    for(int i=0;;(i=i+1)%=n)for(int j=0;j<m;j++)Rbt[i].cmd[j]->update(i,j);
    return 0;
}