개발저장소

[백준 실버 4] 2567번: 색종이 - 2 본문

Coding Test/Baekjoon

[백준 실버 4] 2567번: 색종이 - 2

개발소 2024. 1. 14. 19:59

문제

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

 

2567번: 색종이 - 2

첫째 줄에 색종이의 수가 주어진다. 이어 둘째 줄부터 한 줄에 하나씩 색종이를 붙인 위치가 주어진다. 색종이를 붙인 위치는 두 개의 자연수로 주어지는데 첫 번째 자연수는 색종이의 왼쪽 변

www.acmicpc.net

 

 

풀이

  • 가로, 세로 길이가 100인 도화지 위에 길이가 10인 정사각형 색종이를 도화지와 평행하게 붙인다.
  • 이러한 방식으로 색종이를 여러장 붙였을 때, 둘레의 길이를 구한다.
  • 중간에 구멍이 뚫린 모양도 둘레에 포함한다.

 

수학적인 방법으로 접근하려고 했지만 중간에 둘레가 생기는 경우를 계산하기 위해서 그리디한 방법으로 접근하였다. 도화지를 크기가 100 X 100인 2차원 배열로 생각한다. 이때 배열의 하나의 원소는 1 X 1 크기의 정사각형이 된다. 모든 배열의 위치에서 상하좌우를 탐색하여 색종이가 없는 빈 공간으로 판별되는 경우가 바로 가장자리가 된다.

 

먼저 색종이가 존재하는 부분을 모두 표시하여 준다. boolean 배열로 선언하여 true로 만들어 주었다.

for (int t = 0; t < T; t++) {
    st = new StringTokenizer(br.readLine());
    int x = Integer.parseInt(st.nextToken());
    int y = Integer.parseInt(st.nextToken());

    for (int i = x; i < x + 10; i++) {
        for (int j = y; j < y + 10; j++) {
            paper[i][j] = true;
        }
    }
}

 

그리고 도화지 위를 모두 탐색하며 각 사각형의 위치에서 사방을 탐색한다. 이때, 탐색하는 위치는 색종이가 존재하는 도화지, 즉 2차원 배열이 true인 곳에서 시작한다.

사방 탐색을 하기 위해서는 방향을 표시하는 배열이 추가로 필요하다. 현재 배열을 기준으로 한 칸씩 이동하며 탐색한다.

static int[] dx = { 0, 1, 0, -1 };
static int[] dy = { 1, 0, -1, 0 };

...

if (paper[i][j]) {
    for (int d = 0; d < 4; d++) {
        int nx = i + dx[d];
        int ny = j + dy[d];

		...
    }
}

 

 

이때, 가장자리로 판별하는 기준은 paper[nx][ny]가 false가 되는 경우이다. 또, 색종이가 도화지의 가장자리에 맞닿은 경우, 사방 탐색이 배열의 영역 바깥으로 나갔기 때문에 추가적으로 조건이 필요하다.

if (!paper[nx][ny] || nx < 0 || nx > 100 || ny < 0 || ny > 100)
    border++;

 

 

한 방향이 가장자리라고 판별났더라도 다른 방향이 가장자리가 아니라는 법은 없다. 꼭지점에 해당하는 부분일 수도 있기 때문이다. 그렇기 때문에 모든 방향을 빠짐없이 체크해야 한다.

 

 

전체 코드 (Java)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static int[] dx = { 0, 1, 0, -1 };
	static int[] dy = { 1, 0, -1, 0 };

	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		int T = Integer.parseInt(br.readLine());
		boolean[][] paper = new boolean[101][101];
		int border = 0;

		for (int t = 0; t < T; t++) {
			st = new StringTokenizer(br.readLine());
			int x = Integer.parseInt(st.nextToken());
			int y = Integer.parseInt(st.nextToken());

			for (int i = x; i < x + 10; i++) {
				for (int j = y; j < y + 10; j++) {
					paper[i][j] = true;
				}
			}
		}

		for (int i = 0; i < 100; i++) {
			for (int j = 0; j < 100; j++) {
				if (paper[i][j]) {
					for (int d = 0; d < 4; d++) {
						int nx = i + dx[d];
						int ny = j + dy[d];

						if (!paper[nx][ny] || nx < 0 || nx > 100 || ny < 0 || ny > 100)
							border++;
					}
				}

			}
		}

		System.out.println(border);

	}

}

 

 

후기

그리디하게 생각하지 않고 수학적으로 접근하여 답을 찾아내려고 하다가 시간을 생각보다 많이 썼다. 입력값의 범위를 보고 좀 더 유연하게 생각할 수 있도록 해야겠다.