알고리즘
[BOJ] - 치킨 배달 / 15686번 (Java)
JinHxxxxKim
2024. 2. 20. 17:44
[BOJ] - 치킨 배달 / 15686번 (Java)
1. 문제 접근
0, 1(고객의 집), 2(치킨 집)으로 이루어진 도시의 정보를 입력 받았을 때, 각 고객의 집으로부터 거리의 합이 최소가 되는 M개의 치킨집을 선택하는 문제다.
입력값을 보면 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)으로 M의 최대 값이 13이고, 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다는 말이 있다.
따라서 주어지는 치킨집은 최대 13개, M은 13보다 작거나 같다고 정리할 수 있다.
치킨집의 개수가 최대 13개라는 것은 13 Combination 6 or 7을 해도 경우의 수가 1,716개 밖에 되지 않는다.
따라서 조합을 통해 선택할 치킨 집을 구한 뒤, 모든 고객의 집에 대해 최소 거리를 구하면 된다.
// 각 집에서 모든 치킨집까지의 거리 중 최소값을 더한다.
for (Pos client : clientPos) {
// 개인에 대한 치킨집까지의 최소거리
int personalDist = Integer.MAX_VALUE;
for (int idx = 0; idx < M; ++idx) {
// 현재 살펴볼 치킨 집
Pos chicken = chickenPos[selected[idx]];
// 이전에 계산한 치킨집에 대한 거리와 비교
personalDist = Math.min(personalDist,
Math.abs(client.x - chicken.x) + Math.abs(client.y - chicken.y));
}
// 도시 치킨거리에 개인 치킨거리 합산
localDistSum += personalDist;
}
// 최소값 선택
ans = Math.min(ans, localDistSum);
2. 코드
package boj;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
/**
* [BOJ] - 치킨 배달 / 15686번
* @author JinHxxxxKim
*
* 1. N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)을 입력받는다.
* 2. 도시의 정보를 입력받는다.
* - 치킨집의 정보, 집의 정보를 별도로 저장한다.
* 3. 총 치킨집의 수 중에서 M개의 치킨집을 선택한다.(조합)
* 4. Combination
* - selectIdx == M or elementIdx == 치킨집의 총 개수 (기저조건)
* - 각 집까지의 거리의 합을 구하고 갱신한다.
*/
public class Sol_15686 {
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static StringBuilder sb = new StringBuilder();
private static StringTokenizer st;
private static int N, M;
private static Pos[] chickenPos;
private static Pos[] clientPos;
private static int[][] map;
private static int chickenNum;
private static int clientNum;
private static int[] selected;
private static int ans;
public static void main(String[] args) throws Exception {
st = new StringTokenizer(br.readLine().trim());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new int[N][N];
for (int row = 0; row < N; ++row) {
st = new StringTokenizer(br.readLine().trim());
for (int col = 0; col < N; ++col) {
map[row][col] = Integer.parseInt(st.nextToken());
if (map[row][col] == 1) {
++clientNum;
} else if (map[row][col] == 2) {
++chickenNum;
}
}
}
// 치킨집의 위치를 저장하는 배열
chickenPos = new Pos[chickenNum];
// 고객의 위치를 저장하는 배열
clientPos = new Pos[clientNum];
int chickenIdx = 0;
int clientIdx = 0;
for (int row = 0; row < N; ++row) {
for (int col = 0; col < N; ++col) {
if (map[row][col] == 1) {
clientPos[clientIdx++] = new Pos(row, col);
} else if (map[row][col] == 2) {
chickenPos[chickenIdx++] = new Pos(row, col);
}
}
}
selected = new int[M];
ans = Integer.MAX_VALUE;
combination(0, 0);
System.out.println(ans);
}
private static void combination(int selectIdx, int elementIdx) {
// 기저조건1
if (selectIdx == M) {
// 해당 조합에서의 거리의 합
int localDistSum = 0;
// 각 집에서 모든 치킨집까지의 거리 중 최소값을 더한다.
for (Pos client : clientPos) {
// 개인에 대한 치킨집까지의 최소거리
int personalDist = Integer.MAX_VALUE;
for (int idx = 0; idx < M; ++idx) {
// 현재 살펴볼 치킨 집
Pos chicken = chickenPos[selected[idx]];
// 이전에 계산한 치킨집에 대한 거리와 비교
personalDist = Math.min(personalDist,
Math.abs(client.x - chicken.x) + Math.abs(client.y - chicken.y));
}
// 도시 치킨거리에 개인 치킨거리 합산
localDistSum += personalDist;
}
// 최소값 선택
ans = Math.min(ans, localDistSum);
return;
}
// 기저조건2
if (elementIdx == chickenNum) {
return;
}
// 전처리
selected[selectIdx] = elementIdx;
// 선택O
combination(selectIdx + 1, elementIdx + 1);
// 선택X
combination(selectIdx, elementIdx + 1);
}
static class Pos {
int x, y;
public Pos(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
}
3. 디버깅
치킨 집의 개수가 최대 13개 밖에 되지 않는 입력을 통해 조합을 통해 구현 방향을 잡았다.
기본적인 조합 코드 중 기저조건에 대해 해당 조합이 유효한 조합(최소 거리를 갖는 치킨집의 조합인가)인지 판단하는 로직이 중요하다.