题解:P16398 [ECUSTPC 2026 Spring] 迷路日

题目传送门

思路

因为只能向右转,所以四个方向的出现顺序必然是\(\rightarrow\)\(\rightarrow\)\(\rightarrow\) 西 \(\rightarrow\)\(\rightarrow \cdots\)(循环出现)。

考虑使用一个 map 将这四个方向映射成数字:

方向 数字
N \(0\)
E \(1\)
S \(2\)
W \(3\)

注意到“右转”就相当于数字加 \(1\) 后对 \(4\) 取模

假设初始方向对应数字为 \(a\),目标方向对应数字为 \(b\),则从 \(a\) 方向转到 \(b\) 方向最少要转 \((b-a) \bmod 4\) 次。


我们需要最小化右转次数,而注意到直行不会影响结果,所以我们按起始点和目标点的相对位置和需要直行的路径段数(显然小于等于 \(2\))分类讨论:

1.起点与终点在同一行/同一列

\(x_l=x_t \lor y_l=y_t\)

此时显然只需要一次性转到正确方向,然后直行即可。

此时因不用考虑直行,不妨直接将起点移到终点位置,转到正确方向即可。

方向 \(d\) 对应的数字为 \(get[d]\),目标方向为 \(e\)(下同),则有当前情况所需步数为 \((get[d]-get[e]) \bmod 4\)

其中方向 \(e\) 取决于起点和终点的相对位置(如若 \(x_l=x_t \land y_l>y_t\),则 \(e\)S)。

2.终点在起点的斜方向(不在同行或同列)

此时想象一下会发现,至少需要两段直行路线和一次中间转向才能到达。我们只需考虑两段路径(更多段数只会增加转向次数,必定不优)。

总右转次数=从初始方向转到第一段方向所需的次数+中间转向次数

此时以终点在起点东南(即 \(x_t>x_l \land y_t<y_l\))举例,则最小代价为 \(\min((get[E]-get[d]+4)%4+(get[S]-get[E]+4)%4,(get[S]-get[d]+4)%4+(get[E]-get[S]+4)%4)\)

其他方向同理。


代码

至此,可写出如下代码,详见注释,注意多测:

AC Code
#include <iostream>
#include <map>
#include <cmath>
using namespace std;
int t,x,y,a,b;
char d;
// 映射map容器
map<char,int> mp;
void solve()
{
    cin >> x >> y >> d >> a >> b;
    // 分类讨论
    if(a==x) // x坐标相等
    {
        if(b<y)
        {
            cout << (mp['S']-mp[d]+4)%4 << "\n";
        }
        else if(b>y)
        {
            cout << (mp['N']-mp[d]+4)%4 << "\n";
        }
        else
        {
            cout << "0\n";
        }
    }
    else if(b==y) // y坐标相等
    {
        if(a<x)
        {
            cout << (mp['W']-mp[d]+4)%4 << "\n";
        }
        else if(a>x)
        {
            cout << (mp['E']-mp[d]+4)%4 << "\n";
        }
        else
        {
            cout << "0\n";
        }
    }
    else if(a>x&&b<y) // 东南
    {
        cout << min((mp['E']-mp[d]+4)%4+(mp['S']-mp['E']+4)%4,(mp['S']-mp[d]+4)%4+(mp['E']-mp['S']+4)%4) << "\n";
    }
    else if(a<x&&b<y) // 西南
    {
        cout << min((mp['W']-mp[d]+4)%4+(mp['S']-mp['W']+4)%4,(mp['S']-mp[d]+4)%4+(mp['W']-mp['S']+4)%4) << "\n";
    }
    else if(a>x&&b>y) // 东北
    {
        cout << min((mp['N']-mp[d]+4)%4+(mp['E']-mp['N']+4)%4,(mp['E']-mp[d]+4)%4+(mp['N']-mp['E']+4)%4) << "\n";
    }
    else if(a<x&&b>y) // 西北
    {
        cout << min((mp['W']-mp[d]+4)%4+(mp['N']-mp['W']+4)%4,(mp['N']-mp[d]+4)%4+(mp['W']-mp['N']+4)%4) << "\n";
    }
}
int main()
{
    // 映射方向对应数字
    mp['N']=0;
    mp['E']=1;
    mp['S']=2;
    mp['W']=3;
    cin >> t;
    while(t--)
    {
        solve();
    }
    return 0;
}
posted @ 2026-05-15 22:54  kevin1426730  阅读(3)  评论(0)    收藏  举报