题解:洛谷 P1205 方块转换

【题目来源】

洛谷:P1205 [USACO1.2] 方块转换 Transformations - 洛谷

【题目描述】

一块 \(n\times n\) 正方形的黑白瓦片的图案要被转换成新的正方形图案。写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式:

  • \(90°\):图案按顺时针转 \(90°\)
  • \(180°\):图案按顺时针转 \(180°\)
  • \(270°\):图案按顺时针转 \(270°\)
  • 反射:图案在水平方向翻转(以中央铅垂线为中心形成原图案的镜像)。
  • 组合:图案在水平方向翻转,然后再按照 \(1∼3\) 之间的一种再次转换。
  • 不改变:原图案不改变。
  • 无效转换:无法用以上方法得到新图案。

如果有多种可用的转换方法,请选择序号最小的那个。

只使用上述 \(7\) 个中的一个步骤来完成这次转换。

【输入】

第一行一个正整数 \(n\)

然后 \(n\) 行,每行 \(n\) 个字符,全部为 @-,表示初始的正方形。

接下来 \(n\) 行,每行 \(n\) 个字符,全部为 @-,表示最终的正方形。

【输出】

单独的一行包括 \(1∼7\) 之间的一个数字(在上文已描述)表明需要将转换前的正方形变为转换后的正方形的转换方法。

【输入样例】

3
@-@
---
@@-
@-@
@--
--@

【输出样例】

1

【核心思想】

  1. 问题分析:给定两个 \(n \times n\) 的黑白矩阵(由 @- 组成),判断从原始矩阵到目标矩阵可以通过哪种最小序号的变换得到。可选变换包括:顺时针旋转 \(90^\circ\)\(180^\circ\)\(270^\circ\)、水平反射、水平反射后再旋转(组合)、不变、无效转换。这是一个矩阵变换 + 枚举验证问题。

  2. 算法选择

    • 坐标映射公式:利用矩阵旋转和反射的坐标变换公式,直接推导每个变换后元素的位置
    • 枚举验证:按序号 \(1 \sim 7\) 依次尝试每种变换,一旦匹配立即返回(保证序号最小)
    • 组合变换分解:将"反射 + 旋转"的组合变换拆分为先反射再调用旋转函数验证
  3. 关键步骤

    • 读取输入\(n\)(矩阵大小)、原始矩阵 \(a\)、目标矩阵 \(c\)
    • 定义变换验证函数
      • 旋转 \(90^\circ\)\(a[i][j] \to c[j][n-1-i]\),即新行 = 原列,新列 = \(n-1-\) 原行
      • 旋转 \(180^\circ\)\(a[i][j] \to c[n-1-i][n-1-j]\)
      • 旋转 \(270^\circ\)\(a[i][j] \to c[n-1-j][i]\)
      • 水平反射\(a[i][j] \to c[i][n-1-j]\)(以中央铅垂线为轴左右翻转)
      • 组合变换:先水平反射得到中间矩阵 \(b\),再验证 \(b\) 是否可通过 \(90^\circ\)/\(180^\circ\)/\(270^\circ\) 旋转得到 \(c\)
      • 不变:直接逐元素比较 \(a\)\(c\)
    • 按序匹配:依次调用 func1func6,匹配则输出对应序号,均不匹配则输出 \(7\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\),每种变换验证需要遍历 \(n^2\) 个元素,最多尝试 6 种变换
    • 空间复杂度:\(O(n^2)\),存储原始矩阵、目标矩阵和反射中间矩阵
  5. 矩阵变换的核心思想

    • 坐标映射而非实际旋转:不构造新矩阵,而是通过坐标公式直接比较,节省空间和复制开销
    • 旋转的周期性\(90^\circ\)\(180^\circ\)\(270^\circ\) 对应不同的行列互换模式,本质上是围绕中心点的坐标置换
    • 反射的对称性:水平反射以中央铅垂线为轴,列坐标变为 \(n-1-j\),行坐标不变
    • 组合变换的顺序性:题目规定"先反射再旋转",因此组合验证必须先做反射再做旋转
    • 贪心选最小序号:按 \(1 \sim 7\) 顺序验证,第一个匹配的即为答案
    • 适用于矩阵几何变换、图像旋转翻转、模式匹配等问题

【算法标签】

普及 #模拟

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

// 定义三个15x15的字符矩阵:
// a - 输入的原始矩阵
// b - 用于存储a经过反射后的矩阵
// c - 输入的需要比较的目标矩阵
char a[15][15], b[15][15], c[15][15];

/**
 * 检查矩阵a是否旋转90度后等于矩阵c
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func1(int n, char a[15][15], char c[15][15])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 检查a旋转90度后的位置是否与c匹配
            if (a[i][j] != c[j][n - 1 - i])
                return false;
        }
    }
    return true;
}

/**
 * 检查矩阵a是否旋转180度后等于矩阵c
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func2(int n, char a[15][15], char c[15][15])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 检查a旋转180度后的位置是否与c匹配
            if (a[i][j] != c[n - 1 - i][n - 1 - j])
                return false;
        }
    }
    return true;
}

/**
 * 检查矩阵a是否旋转270度后等于矩阵c
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func3(int n, char a[15][15], char c[15][15])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 检查a旋转270度后的位置是否与c匹配
            if (a[i][j] != c[n - 1 - j][i])
                return false;
        }
    }
    return true;
}

/**
 * 检查矩阵a是否水平反射后等于矩阵c
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func4(int n, char a[15][15], char c[15][15])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 检查a水平反射后的位置是否与c匹配
            if (a[i][j] != c[i][n - 1 - j])
                return false;
        }
    }
    return true;
}

/**
 * 检查矩阵a水平反射后再旋转是否等于矩阵c
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func5(int n, char a[15][15], char c[15][15])
{
    // 先对a进行水平反射,结果存储在b中
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            b[i][j] = a[i][n - 1 - j];
        }
    }
    // 检查反射后的矩阵b是否旋转90、180或270度后等于c
    if (func1(n, b, c) || func2(n, b, c) || func3(n, b, c))
        return true;
    return false;
}

/**
 * 检查矩阵a是否直接等于矩阵c(无变化)
 * @param n 矩阵大小
 * @param a 原始矩阵
 * @param c 目标矩阵
 * @return 是否匹配
 */
bool func6(int n, char a[15][15], char c[15][15])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            // 直接比较a和c的每个元素
            if (a[i][j] != c[i][j])
                return false;
        }
    }
    return true;
}

int main()
{
    int n;        // 定义变量:矩阵大小
    char c1;      // 定义变量:临时存储输入的字符

    cin >> n;      // 输入矩阵大小

    // 输入原始矩阵a
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cin >> c1;
            a[i][j] = c1;
        }
    }

    // 输入目标矩阵c
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cin >> c1;
            c[i][j] = c1;
        }
    }

    // 依次检查各种变换情况,输出对应的编号
    if (func1(n, a, c))
        cout << 1;
    else if (func2(n, a, c))
        cout << 2;
    else if (func3(n, a, c))
        cout << 3;
    else if (func4(n, a, c))
        cout << 4;
    else if (func5(n, a, c))
        cout << 5;
    else if (func6(n, a, c))
        cout << 6;
    else
        cout << 7;  // 不匹配任何情况

    return 0;       // 程序正常结束
}

【运行结果】

3
@-@
---
@@-
@-@
@--
--@
1
posted @ 2026-08-03 08:46  团爸讲算法  阅读(16)  评论(0)    收藏  举报