반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
11-07 11:40
관리 메뉴

ImJay

[BOJ/Java] 17136. 색종이 붙이기 본문

알고리즘/BOJ - Java

[BOJ/Java] 17136. 색종이 붙이기

ImJay 2024. 4. 22. 14:20
반응형

[BOJ/Java] 17136. 색종이 붙이기

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net


문제 해석

본 문제는 10x10 크기의 그리드에서 1로 표시된 부분을 최소 개수의 색종이로 모두 덮는 최적의 방법을 찾는 것이다. 사용 가능한 색종이의 크기는 1x1부터 5x5까지 다양하며, 각 크기의 색종이는 최대 5개까지 사용할 수 있다. 문제의 핵심은 최소한의 색종이 사용으로 모든 1을 덮는 것이다.

풀이 과정

  1. 초기화 및 입력 처리: 그리드를 10x10 크기의 배열로 초기화하고, 입력을 받아 해당 배열을 채운다.
  2. 재귀적 탐색: 재귀 함수 recursive를 사용하여 가능한 모든 색종이 위치를 시도한다. x와 y는 그리드의 위치를 나타내며, c는 현재까지 사용된 색종이의 수다.
  3. 색종이 배치 가능성 검사: getPaperSize 함수를 통해 주어진 위치에 배치할 수 있는 최대 크기의 색종이를 찾는다. 이 크기는 색종이가 그리드 경계를 벗어나지 않고, 이미 색종이가 붙어 있는 곳을 덮지 않는 범위에서 결정된다.
  4. 색종이 배치: 가능한 색종이 크기를 찾으면 해당 크기의 색종이를 붙이고, 재귀적으로 다음 위치를 탐색한다. 탐색 후에는 색종이를 제거하여 다른 가능성을 탐색한다.

코드

package edu.ssafy.im.BOJ.Gold.G2.No17136;

import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static int N = 10; // 그리드의 크기
    private static int ans = Integer.MAX_VALUE; // 최소 색종이 개수, 최대값으로 초기화
    private static int[][] map; // 그리드를 나타내는 배열
    private static int SIZE = 5; // 최대 색종이 크기
    private static int[] papers = {0, 5, 5, 5, 5, 5}; // 사용 가능한 각 크기의 색종이 개수

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;

        map = new int[N][N]; // 그리드 초기화
        for (int x = 0; x < N; x++) {
            st = new StringTokenizer(br.readLine());
            for (int y = 0; y < N; y++) {
                map[x][y] = Integer.parseInt(st.nextToken()); // 그리드에 값 입력
            }
        }

        recursive(0, 0, 0); // 재귀적 탐색 시작

        sb.append(ans == Integer.MAX_VALUE ? -1 : ans); // 결과 출력

        bw.write(sb.toString());
        bw.flush();
        bw.close();
    }

    private static void recursive(int x, int y, int c) {
        if (x == 10) { // 모든 행을 다 탐색했을 경우
            ans = Math.min(ans, c); // 최소 색종이 개수 갱신
            return;
        }

        if (map[x][y] == 0) { // 현재 위치에 색종이 필요 없는 경우
            if (y == 9) recursive(x + 1, 0, c); // 다음 행으로 이동
            else recursive(x, y + 1, c); // 같은 행의 다음 열로 이동
        } else if (map[x][y] == 1) { // 현재 위치에 색종이 필요한 경우
            int size = getPaperSize(x, y); // 가능한 색종이 크기를 얻는다
            if (size == -1) return; // 색종이를 붙일 수 없는 경우, 종료

            for (int i = 1; i <= size; i++) {
                if (papers[i] > 0) {
                    for (int nx = 0; nx < i; nx++) {
                        for (int ny = 0; ny < i; ny++) {
                            map[x + nx][y + ny] = 0; // 색종이 붙이기
                        }
                    }

                    papers[i]--; // 사용한 색종이 개수 감소

                    if (y + i >= 10) recursive(x + 1, 0, c + 1); // 색종이를 붙이고 다음 위치로 이동
                    else recursive(x, y + i, c + 1);

                    for (int nx = 0; nx < i; nx++) {
                        for (int ny = 0; ny < i; ny++) {
                            map[x + nx][y + ny] = 1; // 색종이 제거
                        }
                    }

                    papers[i]++; // 사용했던 색종이 복원
                }
            }
        }
    }

    private static int getPaperSize(int nx, int ny) {
        for (int size = SIZE; size > 0; size--) { // 최대 크기에서부터 시작하여 가능한 색종이 크기를 찾는다
            boolean flag = true;
            L:
            for (int x = nx; x < nx + size; x++) {
                for (int y = ny; y < ny + size; y++) {
                    if (x < 0 || y < 0 || x >= N || y >= N || map[x][y] == 0) {
                        flag = false;
                        break L;
                    }
                }
            }
            if (flag) return size; // 적합한 크기를 찾은 경우
        }
        return -1; // 적합한 크기를 찾지 못한 경우
    }
}

시간 복잡도 분석

본 알고리즘의 시간 복잡도는 색종이를 붙이는 모든 경우의 수를 탐색하기 때문에 최악의 경우 매우 높다. 각 위치에서 최대 5x5 크기의 색종이를 붙일 수 있으며, 이러한 경우의 수가 겹치는 경우가 많다. 재귀적으로 모든 가능성을 탐색하므로 시간 복잡도는 지수적으로 증가할 수 있다.

느낀점

이 문제를 통해 완전 탐색과 백트래킹의 중요성을 다시 한 번 깨달았다. 최적의 해를 찾기 위해서는 모든 가능성을 고려해야 하며, 효율적인 백트래킹을 통해 불필요한 계산을 줄일 필요가 있다는 것을 배웠다. 또한, 문제의 조건을 세심하게 분석하고 이를 코드에 반영하는 능력이 중요함을 느꼈다.

반응형

'알고리즘 > BOJ - Java' 카테고리의 다른 글

[BOJ/Java] 17471. 게리맨더링  (0) 2024.04.22
[BOJ/Java] 17219. 비밀번호 찾기  (0) 2024.04.22
[BOJ/Java] 16637. 괄호 추가하기  (0) 2024.04.22
[BOJ/Java] 3190. 뱀  (0) 2024.04.22
[BOJ/Java] 14500. 테트로미노  (0) 2024.04.22
Comments