[BOJ] 3109번 : 빵집

JoonYoung Maeng·2020년 12월 9일
0
post-thumbnail

image

🔗 문제 링크

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


🤔 문제

유명한 제빵사 김원웅은 빵집을 운영하고 있다. 원웅이의 빵집은 글로벌 재정 위기를 피해가지 못했고, 결국 심각한 재정 위기에 빠졌다.

원웅이는 지출을 줄이고자 여기저기 지출을 살펴보던 중에, 가스비가 제일 크다는 것을 알게되었다. 따라서 원웅이는 근처 빵집의 가스관에 몰래 파이프를 설치해 훔쳐서 사용하기로 했다.

빵집이 있는 곳은 R*C 격자로 표현할 수 있다. 첫째 열은 근처 빵집의 가스관이고, 마지막 열은 원웅이의 빵집이다.

원웅이는 가스관과 빵집을 연결하는 파이프를 설치하려고 한다. 빵집과 가스관 사이에는 건물이 있을 수도 있다. 건물이 있는 경우에는 파이프를 놓을 수 없다.

가스관과 빵집을 연결하는 모든 파이프라인은 첫째 열에서 시작해야 하고, 마지막 열에서 끝나야 한다. 각 칸은 오른쪽, 오른쪽 위 대각선, 오른쪽 아래 대각선으로 연결할 수 있고, 각 칸의 중심끼리 연결하는 것이다.

원웅이는 가스를 되도록 많이 훔치려고 한다. 따라서, 가스관과 빵집을 연결하는 파이프라인을 여러 개 설치할 것이다. 이 경로는 겹칠 수 없고, 서로 접할 수도 없다. 즉, 각 칸을 지나는 파이프는 하나이어야 한다.

원웅이 빵집의 모습이 주어졌을 때, 원웅이가 설치할 수 있는 가스관과 빵집을 연결하는 파이프라인의 최대 개수를 구하는 프로그램을 작성하시오.

📌 입력

첫째 줄에 R과 C가 주어진다. (1 ≤ R ≤ 10,000, 5 ≤ C ≤ 500)

다음 R개 줄에는 빵집 근처의 모습이 주어진다. '.'는 빈 칸이고, 'x'는 건물이다. 처음과 마지막 열은 항상 비어있다.


😮 문제 해결 방법

가장 왼쪽 원웅이네 가스관에서 시작하여 가장 오른쪽 근처 빵집 가스관으로 벽과 다른 파이프와 겹치치않고 접하지 않게 가는 것이므로 깊이 우선 탐색을 통해서 파이프가 설치될 때마다 gas 배열에 x표시를 하는 방식으로 접근하였다.

문제에서 가스관은 접하거나 겹칠수 없고 연결 할 수 있는 방향을 제시해 주었는데 이 뜻은 해당 위치가 X가 아니고 .인 경우에만 지나갈 수 있다라는 뜻으로 해석하면 될 것 같다.

파이프가 설치되는 구역(gas배열이 .인 경우)을 지나갈 때마다 해당 구역에 X를 표시하여 방문 처리를 해주었고 다른 빵집 가스관에 도착시 boolean 타입의 end변수를 true로 해주면서 끝에 제대로 도착했는지 체크하여 true 인 경우 1을 반환하여 설치 가능 갯수를 카운팅 해주었다.

파이프의 설치는 오른쪽 대각선 위, 오른쪽, 오른쪽 대각선 아래 순서로 파이프를 설치하도록 구현하는게 제일 효율적이라 생각해서 해당 순서로 코드를 구현하였다

image


🚩 Java 코드

package com.algorithm.Baekjoon;

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

public class Main_3109 {
    private static int R,C;
    private static final int[] DX = {-1,0,1};
    private static boolean end = false;

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

        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());
        String[][] gas = new String[R][C];

        int count = 0;
        for(int i=0;i<R;i++){
            String[] s = br.readLine().split("");
            for(int j=0;j<C;j++){
                gas[i][j] = s[j];
            }
        }

        for(int i=0;i<R;i++){
            count += dfs(gas,i);
        }

        bw.write(String.valueOf(count));
        br.close();
        bw.close();
    }

    private static int dfs(String[][] gas, int i) {
        end = false;
        boolean result = dfsUtil(gas,i,0);
        return result ? 1 : 0;
    }
    //대각선 위부터 차례로 아래 탐색
    private static boolean dfsUtil(String[][] gas, int x, int y) {
        //끝부분까지 제대로 도착한 경우
        if(y==C-1 && gas[x][y].equals("."))
            end = true;
        
        //파이프 설치 후 gas 배열에 방문 처리
        gas[x][y] = "x";
        for (int j : DX) {
            int nx = x + j;
            int ny = y + 1;
            if (ny < C && nx >= 0 && nx < R && !gas[nx][ny].equals("x")) {
                //끝부분 도착 경우에만 for문 탈출
                if (dfsUtil(gas, nx, ny))
                    break;
            }
        }
        return end;
    }
}

💬 코멘트

문제 분류를 보면 그리디, 백트래킹, 그래프 탐색 등 여러가지 방법이 있는데 그리디 알고리즘이 적용되는 이유는 자세히 모르겠다.

✔ 혹시 간단하게 설명가능하신 분 계시면 댓글 달아주시면 감사하겠습니다! 👍

profile
백엔드 개발자 지망생입니다!

1개의 댓글

comment-user-thumbnail
2021년 3월 4일

안녕하세요, 제가 이해한 대로 왜 그리디인지 간단하게 설명드리려고 답장 남겼습니다. 우선 그리디란 현재 우선적으로 최적으로 선택한 것이 미래에 영향을 미치지 않는 것을 그리디적인 알고리즘이라 할 수 있습니다. 글쓴이 분이 생각하신 대각선으로 우선적으로 선택하여 먼저 가는 것은 결과적으로 다른 파이프들이 이동하는 데에 어떠한 영향도 미치지 않습니다. 이런 점에서 알고리즘 분류가 그리디로 분류 되어 있는 것 같습니다. 즉, 작성해 주신 방법이 그리디적인 알고리즘이라 할 수 있겠습니다. 덕분에 풀이 방법도 알게 되는 군요 감사합니다.

답글 달기