티스토리 뷰

알고리즘/브루트포스

C++ 14500 테트로미노

신입사원 성장기 2023. 7. 24. 00:53

https://www.acmicpc.net/problem/14500

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변

www.acmicpc.net

 

문제 풀이

시간 복잡도를 계산하면 모든 점의 좌표에서 5가지 테트로미노의 회전과 대칭을 고려한 경우의 수를 모두 계산하므로 대략 n*m*5(5가지 테트로미노)*10(회전과 대칭을 고려했을 때 가능한 경우가 대략 10개 정도라고 가정)*4(4개의 정사각형) 정도가 된다. 즉 상한을 구하면 500 * 500 * 5 * 10 * 4 = 50,000,000으로 모든 경우를 2초안에 여유롭게 계산이 가능하다. 

따라서 브루트포스로 풀 수 있음을 확인하고 풀이를 시작하였다.

 

1. 각 좌표에서 가능한 모든 테트로미노 경우의 수를 확인하여 최대값 갱신

이게 끝이다. 

 

코드

#include <bits/stdc++.h>
using namespace std;
int board[501][501];
int n, m;
int ans;
vector<vector<vector<int>>> dx = {
        {{0, 1, 2, 3},
         {0, 0, 0, 0}},

        {{0, 0, 1, 1}},

        {{0, 0, 0, 1},
         {0, 0, 1, 2},
         {0, 0, 0, -1},
         {0, 1, 2, 2},
         {0, 1, 1, 1},
         {0, 1, 2, 2},
         {0, 0, 1, 2},
         {0, 0, 0, 1}
         },

        {{0, 0, 1, 1},
         {0, 0, -1, -1},
         {0, 0, -1, 1},
         {0, 1, 1, 2}},

        {{0, 1, 1, 2},
         {0, 0, -1, 1},
         {0, 0, 0, 1},
         {0, 0, 0, -1}
        }};
vector<vector<vector<int>>> dy = {
        {{0, 0, 0, 0},
         {0, 1, 2, 3}},

        {{0, 1, 0, 1}},

        {{0, 1, 2, 2},
         {0, 1, 0, 0},
         {0, 1, 2, 2},
         {0, 0, 0, 1},
         {0, 0, 1, 2},
         {0, 0, 0, -1},
         {0, 1, 1, 1},
         {0, 1, 2, 0}
         },

        {{0, 1, 1, 2},
         {0, 1, 1, 2},
         {0, 1, 1, 0},
         {0, 0, 1, 1}},

        {{0, 0, 1, 0},
         {0, 1, 1, 1},
         {0, 1, 2, 1},
         {0, 1, 2, 1}
        }
};

void check(int y, int x) { // (x, y)에서 가능한 5가지 테트리미노를 모두 확인한다.
    for(int i=0; i<5; i++) {
        for(int j=0; j < dx[i].size(); j++) {
            int tmp = 0;
            for(int k=0; k<dx[i][j].size(); k++) {
                int nx = x + dx[i][j][k];
                int ny = y + dy[i][j][k];
                if(ny >= n || nx >= m || ny < 0 || nx < 0) {
                    tmp = 0;
                    break;
                }
                tmp += board[ny][nx];
            }
            ans = max(ans, tmp);
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin >> board[i][j];

    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            check(i, j);
        }
    }
    cout << ans << '\n';
}

하지만 위의 코드는 dy, dx를 틀리지 않게 잘 작성해야한다. 따라서 시간이 매우 오래걸렸다.

다른 풀이로는 DFS를 이용하여 4칸을 탐색하면 된다. 왜냐하면 한 좌표에서 4칸을 탐색하는 것과 한 좌표에서 테트로미노를 회전, 대칭시키는 것은 동치이기 때문이다. 즉, 4칸을 탐색하는 것을 테트로미노로 채울 수 있다.

여기서 ㅜ모양은 DFS로 탐색이 불가능하기 때문에 따로 처리를 해주었다.

구현한 코드는 다음과 같다.

#include <bits/stdc++.h>
using namespace std;
int board[501][501];
bool visited[501][501];
int n, m;
int ans;
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int tmp;

void dfs(int y, int x, int k) {
    if(k == 3) {
        ans = max(ans, tmp);
        return;
    }
    for(int i=0; i<4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(nx < 0 || ny < 0 || ny >= n || nx >= m || visited[ny][nx]) continue;
        visited[ny][nx] = true;
        tmp += board[ny][nx];
        dfs(ny, nx, k+1);
        tmp -= board[ny][nx]; // backtrack
        visited[ny][nx] = false; // backtrack
    }
    
    // ㅜ, ㅗ, ㅏ, ㅓ가 보드판을 벗어나지 않는 범위에서 계산
    if(y < n-1 && x < m-2) { // ㅜ
        ans = max(ans, board[y][x] + board[y][x+1] + board[y][x+2] + board[y+1][x+1]);
    }
    if(y < n-1 && x > 0 && x < m-1) { // ㅗ
        ans = max(ans, board[y][x] + board[y+1][x] + board[y+1][x-1] + board[y+1][x+1]);
    }
    if(y < n-2 && x < m-1) { // ㅏ
        ans = max(ans, board[y][x] + board[y+1][x] + board[y+2][x] + board[y+1][x+1]);
    }
    if(y < n-2 && x > 0) { // ㅓ
        ans = max(ans, board[y][x] + board[y+1][x] + board[y+1][x-1] + board[y+2][x]);
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin >> board[i][j];

    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            visited[i][j] = true;
            tmp += board[i][j];
            dfs(i, j, 0);
            tmp -= board[i][j]; // backtrack
            visited[i][j] = false; // backtrack
        }
    }
    cout << ans;
}

'알고리즘 > 브루트포스' 카테고리의 다른 글

C++ 1248 Guess  (0) 2023.07.31
C++ 14501 퇴사  (0) 2023.07.29
C++ 10972 다음 순열  (0) 2023.07.29
C++ 1748 수 이어 쓰기 1  (0) 2023.07.27
C++ 백준 1107 리모컨  (0) 2023.07.23
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday