[JAVA] 백준 (골드5) 15686번 치킨 배달

AIR·2024년 10월 6일

코딩 테스트 문제 풀이

목록 보기
140/194

링크

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


문제 설명

정답률 46.180%
크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.

이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 "치킨 거리"라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.

임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 r1r2+c1c2|r1-r2| + |c1-c2|로 구한다.

도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.


입력 예제

5 3
0 0 1 0 0
0 0 2 0 1
0 1 2 0 0
0 0 1 0 0
0 0 0 0 2

출력 예제

5

풀이

문제 해석부터 하면, 지도에서 2에 해당하는 좌표가 치킨집이며 치킨집들에서 1에 해당하는 집과의 거리가 최소가 되는 M개의 치킨집을 선택하고 그때의 최솟값을 구해야 한다.

언뜻보면 최소거리를 구해야하므로 BFS 문제라 생각할 수 있지만 M개의 치킨집을 고른 다음 나머지 치킨집은 폐업시켜야 하기 때문에, 가지치기를 하며 불필요한 탐색을 줄여야 한다.

우선 집과 치킨집의 개수는 정해지지 않았으므로 List로 저장한다.

for (int i = 0; i < N; i++) {
    st = new StringTokenizer(br.readLine());
    for (int j = 0; j < N; j++) {
        map[i][j] = Integer.parseInt(st.nextToken());
        if (map[i][j] == 1) {
            houses.add(new int[]{i, j});
        } else if (map[i][j] == 2) {
            chickens.add(new int[]{i, j});
        }
    }
}

그리고 여러 개의 치킨집중 M개를 선택해야 하므로 선택 여부를 저장할 불리언 배열도 생성한다.

boolean[] selected = new boolean[chickens.size()];

그리고 백트래킹을 수행하는데 모든 치킨집에 대해 탐색을 하며, M개의 치킨집을 선택할 때까지 재귀를 호출한다. M개의 치킨집을 선택한 후에는 치킨집들로 각 집들까지의 거리를 구하고 최솟값으로 갱신한다.

static void backtracking(int start, int depth) {
    if (depth == M) {
        //치킨 거리 계산 후 최솟값 갱신
        result = Math.min(result, getDistance());
        return;
    }
    for (int i = start; i < chickens.size(); i++) {
        selected[i] = true;  //치킨집 선택
        backtracking(i + 1, depth + 1);  //다음 치킨집 선택
        selected[i] = false;  //선택 해제
    }
}

선택된 치킨집들에서 모든 집까지의 거리를 계산할 때는, 모든 집에 대해 탐색을 하면서 선택된 집에 대해서 거리를 구하고 최소가 되는 치킨집일 때로 값을 갱신해나간다.

//선택된 치킨집들에 대한 도시의 치킨 거리 계산
static int getDistance() {
    int total = 0;
    //모든 집에 대하여 탐색
    for (int[] house : houses) {
        int min = Integer.MAX_VALUE;
        int index = 0;
        for (int[] chicken : chickens) {
            //선택된 치킨집일 경우 집과 치킨집의 거리를 구하고 최솟값 갱신
            if (selected[index++]) {
                int distance = Math.abs(house[0] - chicken[0])
                        + Math.abs(house[1] - chicken[1]);
                min = Math.min(min, distance);
            }
        }
        total += min;
    }
    return total;
}

전체 코드

//백준
public class Main {

    static int[][] map;
    static int N, M, result;
    static List<int[]> houses = new ArrayList<>();
    static List<int[]> chickens = new ArrayList<>();
    static boolean[] selected;

    public static void main(String[] args) throws IOException {

        System.setIn(new FileInputStream("src/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][N];
        result = Integer.MAX_VALUE;

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if (map[i][j] == 1) {
                    houses.add(new int[]{i, j});
                } else if (map[i][j] == 2) {
                    chickens.add(new int[]{i, j});
                }
            }
        }

        selected = new boolean[chickens.size()];
        backtracking(0, 0);

        System.out.println(result);
    }

    static void backtracking(int start, int depth) {
        if (depth == M) {
            //치킨 거리 계산 후 최솟값 갱신
            result = Math.min(result, getDistance());
            return;
        }

        for (int i = start; i < chickens.size(); i++) {
            selected[i] = true;  //치킨집 선택
            backtracking(i + 1, depth + 1);  //다음 치킨집 선택
            selected[i] = false;  //선택 해제
        }
    }

    //선택된 치킨집들에 대한 도시의 치킨 거리 계산
    static int getDistance() {
        int total = 0;

        //모든 집에 대하여 탐색
        for (int[] house : houses) {
            int min = Integer.MAX_VALUE;
            int index = 0;
            for (int[] chicken : chickens) {
                //선택된 치킨집일 경우 집과 치킨집의 거리를 구하고 최솟값 갱신
                if (selected[index++]) {
                    int distance = Math.abs(house[0] - chicken[0])
                            + Math.abs(house[1] - chicken[1]);
                    min = Math.min(min, distance);
                }
            }
            total += min;
        }

        return total;
    }
}
profile
백엔드

0개의 댓글