UVa 12113 Overlapping Squares 覆盖正方形 二进制表示状态 DFS

题目链接:Overlapping Squares
题目描述:

给定一个 4 × 4 4\times 4 4×4网格中一些 2 × 2 2\times 2 2×2正方形的边界,问能否用不多于 6 6 6 2 ∗ 2 2*2 22正方形还原出来。
输入用 5 5 5行,每行有 10 10 10个字符,最后一个字符为 # \# #表示结尾,其余字符如果为空格表示该位置不是正方形的边界,如果为 ∣ | 表示是正方形竖着的边界,如果为 − - 则代表正方形横着的边界,例如下面的输入
在这里插入图片描述
表示的图形为:
在这里插入图片描述

题解:

我们可以不用考虑每行的结束符,这样一个输入实际上含有的字符数为 5 × 9 = 45 5\times 9 = 45 5×9=45这样我们可以用一个 45 45 45位的二进制来表示输入,用 1 1 1来表示输入的位置是边。例如上图的状态可以表示为:
000000000 000101000 001000100 001101100 000000000 000000000\\000101000\\001000100\\001101100\\000000000 000000000000101000001000100001101100000000000
由于最多使用 6 6 6个正方形,所以我们可以提前将使用一个正方形,两个正方形,三个正方形,…,六个正方形的所有可能的情况枚举出来,然后保存到一个集合中,这样对于每一次的输入只需要检查输入对应的状态是否在集合中出现过即可。
同时并不是每一个位置都能放置正方形,实质上,如果只放一个正方形,那么实际上有九种情况,也就是只有 9 9 9个位置可以防止正方形,我们可以把能够正方形的位置记录下来,这样可以减少枚举的状态数。
在某个位置放置一个正方形之后如何更新二进制状态?在放置一个正方形之后,会有 8 8 8个位置的需要变为 1 1 1(一个正方形由 8 8 8条长度为 1 1 1的边组成),同时有 4 4 4个位置由于被遮挡需要变为 0 0 0(中间的十字架形状的四条边会被遮挡)。
如何根据正方形防止的位置快速找到这 12 12 12个位置呢?我们可以考虑将正方形放在左上角的时这 12 12 12个位置,然后我们只需要将这 12 12 12位置加上当前正方形放置的位置就是当前正方形对应的 12 12 12个位置。

代码:

#include <bits/stdc++.h>

const int LINE_NUM_EACH_CASE = 5;
const int LINE_LENGTH = 9;
const int MAX_SQUARE_NUM = 6;
const int NUM_SQUARE_POSITION = 9;

using namespace std;

char line[LINE_LENGTH * 2];
long long status;
int maxDepth, caseID;
set<long long> allStatus;
int sidePosition[] = {
    
    1, 3, 9, 13, 18, 19, 21, 22}; // 正方形放在左上角其各个边在二进制中对应的位置
int coverPosition[] = {
    
    10, 11, 12, 20}; // 正方形放在左上角时被挡住的地方
int squarePosition[] = {
    
    0, 2, 4, 9, 11, 13, 18, 20, 22}; // 每个正方形的左上角能在的位置
bool vis[NUM_SQUARE_POSITION]; // 表示某个位置是否已经放过正方形了

void addSquare(int i, long long &status)
{
    
    
    // 八条边的位置变为1
    for (int j = 0; j < 8; j++) {
    
     status |= 1LL << (squarePosition[i] + sidePosition[j]); }
    // 被挡住的地方变为0
    for (int j = 0; j < 4; j++) {
    
     status &= ~(1LL << (squarePosition[i] + coverPosition[j])); }
}

void dfs(int nowDepth, long long nowStatus)
{
    
    
    if (nowDepth == maxDepth) {
    
    
        allStatus.insert(nowStatus);
        return;
    }
    for (int i = 0; i < NUM_SQUARE_POSITION; i++) {
    
     // 枚举正方形可以放置的位置
        if (vis[i]) {
    
     continue; }
        long long tempStatus = nowStatus;
        vis[i] = true;
        addSquare(i, nowStatus);
        dfs(nowDepth + 1, nowStatus);
        nowStatus = tempStatus;
        vis[i] = false;
    }

}

void preprocess()
{
    
    
    for (maxDepth = 0; maxDepth <= MAX_SQUARE_NUM; maxDepth++) {
    
     dfs(0, 0); }
}

bool getInput()
{
    
    
    status = 0;
    for (int i = 0; i < LINE_NUM_EACH_CASE; i++) {
    
    
        cin.getline(line, LINE_LENGTH * 2);
        if (line[0] == '0') {
    
     return false; }
        for (int j = 0; j < LINE_LENGTH; j++) {
    
    
            if (line[j] != ' ') {
    
    
                status |= (1LL << (i * LINE_LENGTH + j)); // 1后面的LL不可以省略
            }
        }
    }
    return true;
}

int main()
{
    
    
    ios::sync_with_stdio(false);
    preprocess();
    while (getInput()) {
    
    
        caseID++;
        cout << "Case " << caseID << ": ";
        if (allStatus.count(status)) {
    
     cout << "Yes" << endl; }
        else {
    
     cout << "No" << endl; }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45523675/article/details/129148796
今日推荐