[백준] 15686번 치킨 배달

반응형
반응형

문제

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

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

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

예를 들어, 아래와 같은 지도를 갖는 도시를 살펴보자.

0 2 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 2

0은 빈 칸, 1은 집, 2는 치킨집이다.

(2, 1)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |2-1| + |1-2| = 2, (5, 5)에 있는 치킨집과의 거리는 |2-5| + |1-5| = 7이다. 따라서, (2, 1)에 있는 집의 치킨 거리는 2이다.

(5, 4)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |5-1| + |4-2| = 6, (5, 5)에 있는 치킨집과의 거리는 |5-5| + |4-5| = 1이다. 따라서, (5, 4)에 있는 집의 치킨 거리는 1이다.

이 도시에 있는 치킨집은 모두 같은 프랜차이즈이다. 프렌차이즈 본사에서는 수익을 증가시키기 위해 일부 치킨집을 폐업시키려고 한다. 오랜 연구 끝에 이 도시에서 가장 수익을 많이 낼 수 있는  치킨집의 개수는 최대 M개라는 사실을 알아내었다.

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

이 문제를 풀려면 한가지를 알아야한다.

첫번째는 조합을 알아야한다.

조합은 n개중에 r개를 고르는 문제로

수학에서는 nCr로 사용된다. 예를 들어 5개중에 2개를 고른다고 하면

(5 * 4)/2*1 => 10이 나온다. 즉 10가지의 경우가 나온다.

그리고 나머지는 문제에 맞게 적절하게 하면 쉽게 풀린다.

근데... 조합은 어떻게 될까?

나중에 조합을 따로 포스팅을 하는게 좋을 것 같다.

여기에서는

    private static void comb(int depth,int index, int value) {
        if (depth == m) {
            List<int[]> temp = new ArrayList<>();
            for(int i = 0; i<m;i++) {
                temp.add(new int[]{stores.get(cur[i])[0],stores.get(cur[i])[1]});
            }

            combination.add(temp);
            return;
        }
        if (value == stores.size()) {
         return;
        }
        cur[index] = value;
        comb(depth+1,index+1,value+1);
        comb(depth,index,value+1);
    }

이런식으로 코드를 작성하였다.

여기에서는 디테일하게 어떤식으로 흘러가는지는 설명하지는 않겠지만,

대략적으로 설명하자면

depth같은 경우, nCr에서 r을 담당한다. 즉, 몇개를 구할건지 확인하는 부분이라고 할 수 있다.

index는 현재 조합의 인덱스를 표시했다.

마지막으로 value는 전체를 담당한다. 그러니까 nCr에서 n을 뜻한다.

그래서 전체 값으로 현재 인덱스에 저장될 값을 계속해서 저장하구,

만약 같은 인덱스로 다른 값이 들어오면 값을 바꿔준다.

이렇게 구한 값들을 변환 작업을 거친뒤, 저장해주면 조합은 끝난다.

static public int getSum(List<int[]> candidates) {
        int ans = 0;
        for(int[] house : houses) {
            int temp = (int)1e9;
            for(int[] store : candidates) {
                temp = Math.min(temp, Math.abs(house[0] - store[0]) + Math.abs(house[1] -store[1]));
            }
            ans += temp;
        }
        return ans;
    }

 

이 메소드는 조합 메소드로 구한 값들을 candidate라는 변수로 받은 뒤, 

집과 치킨집의 최소거리를 구한다.

집과 치킨집의 거리가 중요한데, 어떤 치킨집을 폐업을 시켜하는지가 중요하다.

내가 빨간색으로 표시한 부분이 있는데

그 부분을 읽어보면

집들과 치킨집 들중에서 가장 가장 가까운 치킨집과의 거리라고 할 수 있을 것 같다.

집은 (2,0), (4,1), (1,2), (2,3)

치킨 집은 (2,1) (2,2) (4,4)

1 2 2 2
2 3 1 1
6 3 5 3 

위 숫자들은 모든 집과 치킨집들의 차이를 구한 값들이다.

집과 가장 가까운 치킨집이라고 했으니

첫 번째 집은 1

두 번째 집은 2

세 번째 집은 1

네 번째 집은 1

가장 가까운 치킨집의 거리의 값이라 할 수 있다.

이들을 더하면 5가되고 실제로 5가 정답이다.

하지만 위는 조합이 필요없는 예제이고

조합이 필요한 경우는

모든 후보지들중에서 가장 값이 작은 값을 return시켜줘야한다.

int min = (int)1e9;
for(int i = 0; i<combination.size();i++) {
	min = Math.min(min,getSum(combination.get(i)));
}

이런식으로하면 모든 후보지들중에 원하는 값을 구할 수 있다.

 

전체 코드

package _12.Implentation;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Chicken4 {
    static int n,m;
    static int[][] maps;
    static List<int[]> houses = new ArrayList<>();
    static List<int[]> stores = new ArrayList<>();
    static int[] cur;
    static List<List<int[]>> combination = new ArrayList<>();
    private static void comb(int depth,int index, int value) {
        if (depth == m) {
            List<int[]> temp = new ArrayList<>();
            for(int i = 0; i<m;i++) {
                temp.add(new int[]{stores.get(cur[i])[0],stores.get(cur[i])[1]});
            }

            combination.add(temp);
            return;
        }
        if (value == stores.size()) {
         return;
        }
        cur[index] = value;
        comb(depth+1,index+1,value+1);
        comb(depth,index,value+1);
    }

    static public int getSum(List<int[]> candidates) {
        int ans = 0;
        for(int[] house : houses) {
            int temp = (int)1e9;
            for(int[] store : candidates) {
                temp = Math.min(temp, Math.abs(house[0] - store[0]) + Math.abs(house[1] -store[1]));
            }
            ans += temp;
        }
        return ans;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        maps = new int[n][n];
        cur = new int[m];
        for(int i = 0;i<n;i++) {
            for(int j = 0;j<n;j++) {
                maps[i][j] = sc.nextInt();
                if (maps[i][j] == 1) {
                    houses.add(new int[]{i,j});
                }

                if (maps[i][j] == 2) {
                    stores.add(new int[]{i,j});
                }
            }
        }
        comb(0,0,0);

        int min = (int)1e9;
        for(int i = 0; i<combination.size();i++) {
            min = Math.min(min,getSum(combination.get(i)));
        }

        System.out.println(min);
    }

}
반응형

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 1149번 RPG 거리  (0) 2020.09.30
[백준] 3190번 뱀  (0) 2020.09.06
[백준] 1931번 회의실 배정  (0) 2020.08.24
[백준] 1946번 신입 사원  (0) 2020.08.24
[백준] 1783번 병든 나이트  (0) 2020.08.22

댓글

Designed by JB FACTORY