AtCoder Beginner Contest 454

E - LRUD Moving

一、 题意解析

1. 核心任务

在一个 \(N \times N\) 的网格中,你需要找到一条从左上角 \((1, 1)\) 到右下角 \((N, N)\) 的路径。

2. 约束条件

  • 必经点:除了一个给定的障碍点 \((A, B)\) 之外,必须访问网格中的每一个格子。
  • 唯一性:每个格子只能访问一次(哈密顿路径)。
  • 步数固定:总共移动 \(N^2 - 2\) 步。
  • 移动规则:只能上下左右移动。

3. 输入输出

  • 如果存在这样的路径,输出 "Yes" 并输出操作序列(U, D, L, R);
  • 如果不存在,输出 "No"。

二、 算法思路

这道题的精髓在于棋盘染色理论分层构造法

1. 存在性判定(奇偶性检查)

我们将 \(N \times N\) 的格子进行黑白染色,规定 \((i, j)\) 的颜色由 \(i+j\) 的奇偶性决定。

格数规律:总共有 \(N^2\) 个格子。在 \(N\) 为偶数时,黑白格子各占一半(\(N^2/2\))。

路径规律:从 \((1, 1)\) 走到 \((N, N)\),如果访问了所有点,路径长度是 \(N^2 - 1\)

本题关键:我们需要跳过一个点 \((A, B)\),路径长度变为\(N^2 - 2\)

  • \((1, 1)\)\((N, N)\) 的坐标和都是偶数(同色),所以需要经过偶数步。所以当 \(N\) 为奇数的时候,\(N^2 - 2\) 为奇数,矛盾,所以输出No
  • 要从一个颜色出发,走完除去一个点后的所有点并到达同色点。在上面的条件下,\(N\) 为偶数,\(N^2 - 1\) 为需要染色的方格个数 (因为跳过一个了),这个数为奇数,所以按照路径,偶奇偶奇......偶,所以被删除的点\((A, B)\),必然是奇数点,如果是偶数,那就输出No
  • 因此,必须满足:\(N\) 是偶数,且 \(A+B\) 是奇数。否则直接输出 "No"。

2. 构造合法方案

将原问题 \(N \times N\) 逐步缩小,直到变成一个最简的 \(2 \times 2\) 核心区域。

步骤一:行缩减 (Horizontal Strips)

如果目标空洞 \((A, B)\) 不在前两行(\(A > 2\)),我们可以直接走完前两行:

  • 路径:\((1,1) \to (1,N) \to (2,N) \to (2,1) \to (3,1)\)
  • 代码中的 s1 字符串 RRRRRDLLLLLLD 对应的就是这一操作。通过这种方式,问题规模从 \(N \times N\) 缩减为 \((N-2) \times N\)

步骤二:列缩减 (Vertical Strips)

在处理完行后,如果空洞 \((A, B)\) 不在前两列(\(B > 2\)),我们采用类似的逻辑走完最左边的两列:

  • 路径:反复下右上右路径 ,\((1,1) \to (2,1) \to (2,2) \to (2,2) \to (3,1)\)
  • 这样将问题宽度减少 2,直到 \((A, B)\) 落在当前剩余区域的左上角 \(2 \times 2\) 范围内。

第三步:核心 \(2 \times 2\) 绕行

通过上述缩减,最终 \((A, B)\) 必然会落在一个 \(2 \times 2\) 或类似的局部区域内。此时:

  • 如果 \((A, B)\)\((1, 2)\),我们必须先走 D 再走 R 绕开它。
  • 否则,通常先走 R 再走 D

第四步:后续填补

处理完包含 \((A, B)\) 的核心区域后,再用同样对称的逻辑(s3s4)把右边和下面剩下的空间补齐。

代码

#include <bits/stdc++.h>
using namespace std;
void solve() {
    int a, b, n, m, N;
    cin >> n >> a >> b;
    m = n;
    N = n;
    if (n % 2 == 1) {
        cout << "No" << endl;
        return;
    }
    if ((a + b) % 2 == 0) {
        cout << "No" << endl;
        return;
    }
    cout << "Yes" << endl;
    // 1 .f(n,m,a,b)->f(n-2,m,a-2,b) RRRRRDLLLLLLD
    int firstrow = 0;
    string s1;
    while (a > 2) {
        n -= 2;
        a -= 2;
        firstrow++;
    }

    for (int i = 0; i < N - 1; i++) {
        s1.push_back('R');
    }
    s1.push_back('D');
    for (int i = 0; i < N - 1; i++) {
        s1.push_back('L');
    }
    s1.push_back('D');

    // 4. f(n,m,a,b)->f(n-2,m,a,b)  DLLLLLLLLDRRRRRRRRRR
    int secondrow = 0;
    while (n - 1 > a) {
        n -= 2;
        secondrow++;
    }
    string s4;
    s4.push_back('D');
    for (int i = 0; i < N - 1; i++) {
        s4.push_back('L');
    }
    s4.push_back('D');
    for (int i = 0; i < N - 1; i++) {
        s4.push_back('R');
    }

    // 2. f(n,m,a,b)->f(n,m-2,a,b-2)  DRUR
    int firstcol = 0;
    while (b > 2) {
        m -= 2;
        b -= 2;
        firstcol++;
    }
    string s2;
    s2.push_back('D');
    s2.push_back('R');
    s2.push_back('U');
    s2.push_back('R');

    // 3.RURD
    int secondcol = 0;
    while (m - 1 > b) {
        m -= 2;
        secondcol++;
    }
    string s3;
    s3.push_back('R');
    s3.push_back('U');
    s3.push_back('R');
    s3.push_back('D');

    for (int i = 0; i < firstrow; i++) {
        cout << s1;
    }
    for (int i = 0; i < firstcol; i++) {
        cout << s2;
    }

    if (a == 1 && b == 2) {
        cout << "DR";
    } else {
        cout << "RD";
    }

    for (int i = 0; i < secondcol; i++) {
        cout << s3;
    }

    for (int i = 0; i < secondrow; i++) {
        cout << s4;
    }

    cout << endl;
}
int main() {
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}
posted @ 2026-04-21 19:36  Wraith_G  阅读(5)  评论(0)    收藏  举报
// //