문제

문제 설명

테이블 위에 놓인 퍼즐 조각을 게임 보드의 빈 공간에 적절히 올려놓으려 합니다. 게임 보드와 테이블은 모두 각 칸이 1x1 크기인 정사각 격자 모양입니다. 이때, 다음 규칙에 따라 테이블 위에 놓인 퍼즐 조각을 게임 보드의 빈칸에 채우면 됩니다.

  • 조각은 한 번에 하나씩 채워 넣습니다.
  • 조각을 회전시킬 수 있습니다.
  • 조각을 뒤집을 수는 없습니다.
  • 게임 보드에 새로 채워 넣은 퍼즐 조각과 인접한 칸이 비어있으면 안 됩니다.

다음은 퍼즐 조각을 채우는 예시입니다.

puzzle_5.png

위 그림에서 왼쪽은 현재 게임 보드의 상태를, 오른쪽은 테이블 위에 놓인 퍼즐 조각들을 나타냅니다. 테이블 위에 놓인 퍼즐 조각들 또한 마찬가지로 [상,하,좌,우]로 인접해 붙어있는 경우는 없으며, 흰 칸은 퍼즐이 놓이지 않은 빈 공간을 나타냅니다. 모든 퍼즐 조각은 격자 칸에 딱 맞게 놓여있으며, 격자 칸을 벗어나거나, 걸쳐 있는 등 잘못 놓인 경우는 없습니다.

이때, 아래 그림과 같이 3,4,5번 조각을 격자 칸에 놓으면 규칙에 어긋나므로 불가능한 경우입니다.

puzzle_6.png

  • 3번 조각을 놓고 4번 조각을 놓기 전에 위쪽으로 인접한 칸에 빈칸이 생깁니다.
  • 5번 조각의 양 옆으로 인접한 칸에 빈칸이 생깁니다.

다음은 규칙에 맞게 최대한 많은 조각을 게임 보드에 채워 넣은 모습입니다.

puzzle_7.png

최대한 많은 조각을 채워 넣으면 총 14칸을 채울 수 있습니다.

현재 게임 보드의 상태 game_board, 테이블 위에 놓인 퍼즐 조각의 상태 table이 매개변수로 주어집니다. 규칙에 맞게 최대한 많은 퍼즐 조각을 채워 넣을 경우, 총 몇 칸을 채울 수 있는지 return 하도록 solution 함수를 완성해주세요.


제한사항
  • 3 ≤ game_board의 행 길이 ≤ 50
  • game_board의 각 열 길이 = game_board의 행 길이
    • 즉, 게임 보드는 정사각 격자 모양입니다.
    • game_board의 모든 원소는 0 또는 1입니다.
    • 0은 빈칸, 1은 이미 채워진 칸을 나타냅니다.
    • 퍼즐 조각이 놓일 빈칸은 1 x 1 크기 정사각형이 최소 1개에서 최대 6개까지 연결된 형태로만 주어집니다.
  • table의 행 길이 = game_board의 행 길이
  • table의 각 열 길이 = table의 행 길이
    • 즉, 테이블은 game_board와 같은 크기의 정사각 격자 모양입니다.
    • table의 모든 원소는 0 또는 1입니다.
    • 0은 빈칸, 1은 조각이 놓인 칸을 나타냅니다.
    • 퍼즐 조각은 1 x 1 크기 정사각형이 최소 1개에서 최대 6개까지 연결된 형태로만 주어집니다.
  • game_board에는 반드시 하나 이상의 빈칸이 있습니다.
  • table에는 반드시 하나 이상의 블록이 놓여 있습니다.

입출력 예
game_boardtableresult
[[1,1,0,0,1,0],[0,0,1,0,1,0],[0,1,1,0,0,1],[1,1,0,1,1,1],[1,0,0,0,1,0],[0,1,1,1,0,0]][[1,0,0,1,1,0],[1,0,1,0,1,0],[0,1,1,0,1,1],[0,0,1,0,0,0],[1,1,0,1,1,0],[0,1,0,0,0,0]]14
[[0,0,0],[1,1,0],[1,1,1]][[1,1,1],[1,0,0],[0,0,0]]0

입출력 예 설명

입출력 예 #1

입력은 다음과 같은 형태이며, 문제의 예시와 같습니다.

puzzle_9.png

입출력 예 #2

블록의 회전은 가능하지만, 뒤집을 수는 없습니다.

풀이

#include <string>
#include <vector>
#include <queue>
#include <climits>
#include <algorithm>
 
using namespace std;
 
struct Point {
    int x, y;
    Point(int x, int y): x(x), y(y) {}
}; 
 
void normalizePoints(vector<Point> &v) {
    int min_x = INT_MAX;
    int min_y = INT_MAX;
    for (auto p:v) {
        min_x = (min_x < p.x)? min_x : p.x;
        min_y = (min_y < p.y)? min_y : p.y;
    }
    for (auto &p:v) {
        p.x -= min_x;
        p.y -= min_y;
    }
    
    sort(v.begin(), v.end(), [](Point a, Point b) {
        if (a.x==b.x) return a.y < b.y;
        return a.x < b.x;
    });
}
 
void rotatePuzzle(vector<Point> &v) {
    int max_x = 0;
    int max_y = 0;
    for (auto &p:v) {
        max_x = (max_x > p.x)? max_x : p.x;
        max_y = (max_y > p.y)? max_y : p.y;      
    }
    int max_n = (max_x > max_y)? max_x : max_y;
    for (auto &p : v) {
        int old_x = p.x;
        int old_y = p.y;
 
        p.x = max_n - old_y;
        p.y = old_x;
    }
    normalizePoints(v);
}
 
bool checkMatch(vector<Point> s, vector<Point> p) {
    if (s.size()!=p.size()) return false;
    for (int i=0; i<s.size(); i++) {
        if (s[i].x!=p[i].x || s[i].y!=p[i].y) return false;
    }
    return true;
}
 
int solution(vector<vector<int>> game_board, vector<vector<int>> table) {
    int answer = 0;
    
    vector<vector<Point>> space;
    vector<vector<Point>> puzzles;
    
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    
    // 보드 빈 공간 뽑기 bfs
    vector<vector<int>> visit(game_board.size(), vector<int> (game_board.size()));
    queue<Point> q;
    for (int i=0; i<game_board.size(); i++){
        for (int j=0; j<game_board.size(); j++) {
            if (game_board[i][j]==0 && visit[i][j]==0) {
                q.push(Point(j, i));
                visit[i][j]=1;
                vector<Point> area;
                while (!q.empty()) {
                    Point cur = q.front();
                    q.pop();
                    area.push_back(cur);
                    for (int k=0; k<4; k++) {
                        int new_x = cur.x + dx[k];
                        int new_y = cur.y + dy[k];
                        if (new_x < 0 || new_y < 0 || new_x >= game_board.size() || new_y >= game_board.size()) continue;
                        if (visit[new_y][new_x]!=0 || game_board[new_y][new_x]!=0) continue;
                        q.push(Point(new_x, new_y));
                        visit[new_y][new_x]=1;
                    }
                }
                normalizePoints(area);
                space.push_back(area);
            }
        }
    }
    // 퍼즐 조각 뽑기 bfs
    vector<vector<int>> visit_puzzle(game_board.size(), vector<int> (game_board.size()));
    queue<Point> q_puzzle;
    for (int i=0; i<table.size(); i++){
        for (int j=0; j<table.size(); j++) {
            if (table[i][j]==1 && visit_puzzle[i][j]==0) {
                q_puzzle.push(Point(j, i));
                visit_puzzle[i][j]=1;
                vector<Point> puzzle;
                while (!q_puzzle.empty()) {
                    Point cur = q_puzzle.front();
                    q_puzzle.pop();
                    puzzle.push_back(cur);
                    for (int k=0; k<4; k++) {
                        int new_x = cur.x + dx[k];
                        int new_y = cur.y + dy[k];
                        if (new_x < 0 || new_y < 0 || new_x >= table.size() || new_y >= table.size()) continue;
                        if (visit_puzzle[new_y][new_x]!=0 || table[new_y][new_x]!=1) continue;
                        q_puzzle.push(Point(new_x, new_y));
                        visit_puzzle[new_y][new_x]=1;
                    }
                }
                normalizePoints(puzzle);
                puzzles.push_back(puzzle);
            }
        }
    } 
    // 매칭되는지 확인 삼중 for 문
    // 1. 빈공간 2. 퍼즐 3. 회전
    vector<int> space_check(space.size());
    vector<int> puzzle_check(puzzles.size());
    for (int s=0; s<space.size(); s++) { 
        for (int p=0; p<puzzles.size(); p++) {
            auto sp = space[s];
            auto pu = puzzles[p];
            if (space_check[s]) continue;
            if (puzzle_check[p]) continue;
            for (int i=0; i<4; i++) {
                // 매칭 확인
                bool match = checkMatch(sp, pu);
                if (match) {
                    answer+= sp.size();
                    space_check[s] = 1;
                    puzzle_check[p] = 1;
                    break;
                }
                else rotatePuzzle(pu);
            }
        }
    }
    
    return answer;
}