UVa 12113 Overlapping Squares

题目描述

这是一个关于重叠正方形的拼图问题。给定一个目标图案,我们需要判断是否可以通过在 4×44 \times 44×4 的网格上放置 111666 个(包含)2×22 \times 22×2 的实心正方形来形成该图案。

每个 2×22 \times 22×2 正方形在字符网格中表现为 333555 列的边框结构:

  • 水平边框用 '_' 表示
  • 垂直边框用 '|' 表示
  • 内部用空格填充

正方形可以相互重叠,当放置新正方形时,它会覆盖之前正方形在该区域的部分边框。

输入格式

输入包含多个测试用例。每个测试用例包含 555 行,每行包含 999 个字符。字符只能是:

  • '_':水平边框
  • '|':垂直边框
  • ' ':空格
  • '#':行结束标记(不是图案的一部分)

最后一个测试用例后跟一个单独的 000,表示输入结束。

输出格式

对于每个测试用例,输出用例编号和 "Yes""No",表示是否可以用 111666 个正方形形成目标图案。

题目分析

问题本质

这个问题本质上是一个模式匹配问题,但难点在于:

  1. 重叠效应:正方形重叠时,新正方形会覆盖旧正方形的边框
  2. 多种组合:最多 666 个正方形,在 999 个可能位置上的组合数量很大
  3. 边框可见性:需要正确模拟物理上的覆盖规则

关键观察

  1. 网格结构4×44 \times 44×4 的物理网格对应 555999 列的字符网格
  2. 正方形位置2×22 \times 22×2 正方形有 3×3=93 \times 3 = 93×3=9 个可能的放置位置
  3. 覆盖规则
    • 新正方形的边框总是可见
    • 上边界的空格位置需要特殊处理,保留可能穿过的竖边
    • 其他区域直接覆盖

解题思路

方法选择:预处理 + 精确匹配

由于测试用例可能较多,我们采用预处理生成所有可能图案 + 查询时精确匹配的策略。

算法步骤

1. 预处理阶段:生成所有可能图案

使用 DFS\texttt{DFS}DFS 回溯生成所有可能的正方形组合:

  • 状态空间:最多 666 个正方形,每个可在 999 个位置中的任意位置
  • 状态表示:当前网格状态
  • 终止条件:放置了 666 个正方形
  • 优化:每放置一个正方形就记录当前图案
2. 标准化处理

为确保图案比较的一致性,对所有生成的图案进行标准化:

  • 提取包含非空格字符的最小矩形区域
  • 将该区域平移到网格的左上角
3. 查询阶段

对每个测试用例:

  1. 标准化输入图案
  2. 在预生成的图案集合中查找
  3. 输出匹配结果

复杂度分析

  • 时间复杂度
    • 预处理:O(96×标准化时间)O(9^6 \times \text{标准化时间})O(96×标准化时间),实际由于重叠和标准化,重复图案很多
    • 查询:O(1)O(1)O(1) 哈希查找
  • 空间复杂度O(唯一图案数量)O(\text{唯一图案数量})O(唯一图案数量),实践中远小于理论最大值

关键实现细节

  1. 智能覆盖逻辑:正确处理正方形重叠时的边框可见性
  2. 字符串表示:使用完整字符串而非整数编码,避免哈希冲突
  3. 标准化:确保图案比较的一致性

代码实现

// Overlapping Squares
// UVa ID: 12113
// Verdict: Accepted
// Submission Date: 2025-11-28
// UVa Run Time: 0.130s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

// 存储所有可能的图案(使用字符串表示,避免编码冲突)
unordered_set<string> allPatterns;

// 标准2x2正方形的字符模板(3行5列)
const char squareTemplate[3][5] = {
    {' ', '_', ' ', '_', ' '},  // 上边框:两条横线,中间有空格
    {'|', ' ', ' ', ' ', '|'},  // 中间行:左右竖边,内部空格
    {'|', '_', ' ', '_', '|'}   // 下边框:左右竖边和两条横线
};

// 将5x9网格转换为字符串表示
string patternToString(const char grid[5][10]) {
    string result;
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 9; j++)
            result += grid[i][j];
    return result;
}

// 在指定位置放置一个2x2正方形
void placeSquare(char grid[5][10], int pos) {
    int row = pos / 3;  // 计算行位置(0-2)
    int col = pos % 3;  // 计算列位置(0-2)
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 5; j++) {
            int gridRow = row + i;
            int gridCol = col * 2 + j;  // 列位置乘以2,因为每个正方形水平占5个字符
            if (gridRow >= 5 || gridCol >= 9) continue;
            char templateChar = squareTemplate[i][j];
            char currentChar = grid[gridRow][gridCol];
            if (i == 0) {  // 上边界特殊处理
                if (templateChar == '_') 
                    grid[gridRow][gridCol] = '_';  // 横线直接覆盖
                else if (currentChar == '|') 
                    ;  // 保留竖边(竖边穿过上边界)
                else 
                    grid[gridRow][gridCol] = ' ';  // 其他情况用空格覆盖
            } else {
                // 中间行和下边界:直接用模板字符覆盖
                grid[gridRow][gridCol] = templateChar;
            }
        }
    }
}

// 标准化图案:提取有效区域并靠上靠左放置
void normalizePattern(char grid[5][10]) {
    int minRow = 5, maxRow = -1, minCol = 9, maxCol = -1;
    
    // 找到包含非空格字符的最小矩形区域
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 9; j++) {
            if (grid[i][j] != ' ') {
                minRow = min(minRow, i);
                maxRow = max(maxRow, i);
                minCol = min(minCol, j);
                maxCol = max(maxCol, j);
            }
        }
    }
    
    if (minRow > maxRow || minCol > maxCol) return;  // 没有有效字符
    
    // 创建新的标准化网格
    char normalized[5][10];
    memset(normalized, ' ', sizeof(normalized));
    
    // 将有效区域平移到左上角
    for (int i = minRow; i <= maxRow; i++) {
        for (int j = minCol; j <= maxCol; j++) {
            int newI = i - minRow;
            int newJ = j - minCol;
            if (newI < 5 && newJ < 9) 
                normalized[newI][newJ] = grid[i][j];
        }
    }
    
    memcpy(grid, normalized, sizeof(normalized));
}

// DFS回溯生成所有可能的图案组合(最多6个正方形)
void generatePatterns(char grid[5][10], int count) {
    // 标准化当前图案并存储
    char normalized[5][10];
    memcpy(normalized, grid, sizeof(normalized));
    normalizePattern(normalized);
    allPatterns.insert(patternToString(normalized));
    
    if (count == 6) return;  // 最多放置6个正方形
    
    // 尝试在所有9个可能位置放置正方形
    for (int i = 0; i < 9; i++) {
        // 保存当前状态用于回溯
        char backup[5][10];
        memcpy(backup, grid, sizeof(backup));
        
        // 放置正方形并继续搜索
        placeSquare(grid, i);
        generatePatterns(grid, count + 1);
        
        // 回溯:恢复之前的状态
        memcpy(grid, backup, sizeof(backup));
    }
}

// 预生成所有可能的图案
void generateAllPatterns() {
    char grid[5][10];
    memset(grid, ' ', sizeof(grid));  // 初始化为全空格
    generatePatterns(grid, 0);
}

// 标准化输入的目标图案
void normalizeTarget(char target[5][10]) {
    char processed[5][10];
    memset(processed, ' ', sizeof(processed));
    // 去除输入中的#号,只保留有效字符
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 9 && target[i][j] != '#'; j++)
            processed[i][j] = target[i][j];
    // 标准化处理后的图案
    normalizePattern(processed);
    memcpy(target, processed, sizeof(processed));
}

int main() {
    // 预处理:生成所有可能的图案
    generateAllPatterns();
    
    int caseNum = 1;
    string line;
    
    // 处理每个测试用例
    while (true) {
        getline(cin, line);
        if (line == "0") break;  // 输入0结束
        
        // 读取目标图案
        char target[5][10];
        memset(target, ' ', sizeof(target));
        for (int i = 0; i < 5; i++) {
            if (i > 0) getline(cin, line);
            for (int j = 0; j < 9; j++) 
                target[i][j] = line[j];
        }
        
        // 标准化目标图案并在预生成的图案集合中查找
        normalizeTarget(target);
        string targetStr = patternToString(target);
        bool found = allPatterns.count(targetStr);
        
        // 输出结果
        cout << "Case " << caseNum++ << ": " << (found ? "Yes" : "No") << endl;
    }
    
    return 0;
}

总结

本题的关键在于正确模拟正方形重叠的物理效果,并通过预处理优化查询性能。使用 DFS\texttt{DFS}DFS 回溯生成所有可能组合,配合标准化处理和字符串精确匹配,既保证了正确性又获得了良好的性能。

这种预处理 + 查询的模式在解决此类组合搜索问题时非常有效,特别是当测试用例较多时,能够将计算密集型工作转移到预处理阶段,显著提高整体效率。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值