https://www.acmicpc.net/problem/15686
문제
N*N 크기의 도시지도가 있다. 각 격자칸에는 0은 빈칸, 1은 집, 2는 피자집으로 표현된다. 각 격자칸은 좌표(행번호, 열번호)로 표시된다. 도시에는 각 집마다 피자배달거리가 있는데, 각 집의 피자 배달 거리는 해당 집과 도시에 존재하는 피자집들과의 거리중 최솟값이다.
그런데 경기가 불황이라 도시에 있는 피자 집 중에서 M개만 남기고 나머지는 보조금을 주고 폐업시키려고한다.
이때 M개의 피자집을 고르는 기준은 도시의 피자 배달 거리가 최소가 되는 값이다.
도시의 피자 배달거리란 각 집들의 피자 배달 거리를 모두 합한 값이다.
입력
첫번째 줄에 N과 M이 주어진다.
두번째줄 부터 도시 정보가 입력된다.
출력
첫째줄에 M개의 피자집이 선택되었을때 , 도시의 최소 배달 거리를 출력한다.
입력 예시
4 4
0 1 2 0
1 0 2 1
0 2 1 2
2 0 1 2
출력 예시
6
문제 풀이
static class Point {
int i;
int j;
Point(int i,int j){
this.i=i;
this.j=j;
}
}
- 도시의 정보(집,피자집의 좌표)를 저장할 사용자 변수 선언
static int n,m;
static int[][] arr;
static List<Point> homes = new ArrayList<>(); // 집의 좌표 저장
static List<Point> all_pizza= new ArrayList<>(); // 모든 피자집의 좌표 저장
static Point[] selected; // 선택된 M개의 피자집 좌표 저장
static int answer = Integer.MAX_VALUE; // 최종 최소 합
- List를 사용한 이유는 가변 길이 자료형 이기 때문이다. 배열로 선언한다면 도시 정보를 입력 받을때 집은 몇 개인지, 피자집은 몇 개인지 따로 변수를 설정하여 개수를 세어야하기 때문이다. 그래서 정보를 입력 받으면서 동시에 집의 좌표와 피자집의 좌표를 구분하여 저장할 수 있도록 homes와 all_pizza를 List 자료형을 이용하여 저장하였다.
이 문제에서 가장 중요한 것은 결국 도시 정보로 주어진 피자집 중에서 M개를 선택했을때, 어떤 경우에 도시의 피자 배달 거리 값이 최소가 될 수 있는지 구하는 것이다.
따라서 문제 풀이 순서는 아래와 같다.
- 도시 정보로 주어진 피자집 중에서 M개를 선택하는 조합을 만든다.
- 조합이 선택되면, 각각의 조합에서 도시의 피자 배달 거리를 구한다.
- 각각의 조합에서 구한 도시 피자 배달 거리 중 최솟값을 찾는다.
Main 코드
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());
n = Integer.parseInt(st.nextToken()); // 도시의 크기 N*N
m = Integer.parseInt(st.nextToken()); // 피자집의 개수
// 배열 정보 입력하기
arr = new int[n][n];
selected = new Point[m];
// 도시 정보 입력하기
for(int i=0;i<n;i++){
st = new StringTokenizer(br.readLine());
for(int j=0;j<n;j++){
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
// 집과 피자집 위치 로드
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (arr[i][j] == 1) { // 집
homes.add(new Point(i, j));
} else if (arr[i][j] == 2) { // 피자집
all_pizza.add(new Point(i, j));
}
}
}
// 집의 피자 배달거리를 구하기
dfs(0,0);
System.out.println(answer);
}
- 먼저 Main 코드에서는 입력 예시와 같이, 도시의 크기와 선택될 피자집의 개수를 입력 받는다.
- 이후 도시의 정보를 배열에 입력하고, 집과 피자집의 정보를 List 자료형에 따로 저장해둔다.
- 마지막에 dfs 를 호출하여 계산을 한다.
dfs 코드
static void dfs(int start,int L){
if(L==m){
// M개를 다 골랐으면 거리 계산하고 최소값 갱신
int sum=0;
for (Point home : homes) {
int minDist = Integer.MAX_VALUE;
for (Point p : selected) { // 선택된 M개의 피자집으로 피자 배달거리 구하기
int dist = Math.abs(home.i - p.i) + Math.abs(home.j - p.j);
minDist = Math.min(minDist, dist); // 집의 피자 배달거리
}
sum+=minDist;// 도시의 피자 배달거리
}
answer = Math.min(answer,sum); // M개의 피자집 -> 도시의 최소 배달거리
}
for(int i=start;i<all_pizza.size();i++){
selected[L]=all_pizza.get(i);
dfs(i + 1, L + 1);
}
}
- 이 코드의 전체적인 틀은 사실 "조합"을 구하는 코드와 동일하다.
- 피자집의 정보(all_pizza)를 이용하여 조합을 구한다. 이후 M개의 피자집을 모두 선택했다면, 집의 피자 배달 거리를 구하고 도시의 최소 배달 거리를 구한다.
- 그리고 answer라는 전역 변수를 통해, 각각의 조합의 도시의 최소 배달 거리를 비교하며 최솟값을 저장한다.
전체 코드
package 섹션8_DFS_BFS심화;
import java.io.*;
import java.util.*;
public class Q14_피자배달거리 {
static int n,m;
static int[][] arr;
static List<Point> homes = new ArrayList<>();
static List<Point> all_pizza= new ArrayList<>(); // 모든 피자집
static Point[] selected; // 선택된 M개의 피자집
static int answer = Integer.MAX_VALUE; // 최종 최소 합
static void dfs(int start,int depth){
if(depth==m){
// M개를 다 골랐으면 거리 계산하고 최소값 갱신
int sum=0;
for (Point home : homes) {
int minDist = Integer.MAX_VALUE;
for (Point p : selected) { // 선택된 M개의 피자집으로 피자 배달거리 구하기
int dist = Math.abs(home.i - p.i) + Math.abs(home.j - p.j);
minDist = Math.min(minDist, dist); // 집의 피자 배달거리
}
sum+=minDist;// 도시의 피자 배달거리
}
answer = Math.min(answer,sum);
}
for(int i=start;i<all_pizza.size();i++){
selected[depth]=all_pizza.get(i);
dfs(i + 1, depth + 1);
}
}
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());
n = Integer.parseInt(st.nextToken()); // 도시의 크기 N*N
m = Integer.parseInt(st.nextToken()); // 피자집의 개수
// 배열 정보 입력하기
arr = new int[n][n];
selected = new Point[m];
// 도시 정보 입력하기
for(int i=0;i<n;i++){
st = new StringTokenizer(br.readLine());
for(int j=0;j<n;j++){
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
// 집과 피자집 위치 로드
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (arr[i][j] == 1) { // 집
homes.add(new Point(i, j));
} else if (arr[i][j] == 2) { // 피자집
all_pizza.add(new Point(i, j));
}
}
}
// 집의 피자 배달거리를 구하기
dfs(0,0);
System.out.println(answer);
}
static class Point {
int i;
int j;
Point(int i,int j){
this.i=i;
this.j=j;
}
}
}
'알고리즘 > 자바(Java) 알고리즘 문제풀이 입문: 코딩테스트 대비' 카테고리의 다른 글
85강,86강 섬나라 아일랜드(BFS,DFS) (1) | 2025.06.27 |
---|---|
84강 토마토 (0) | 2025.06.26 |
83강 미로의 최단 거리 통로 (4) | 2025.06.21 |
82강 미로탐색 (2) | 2025.06.21 |
메모이제이션 (0) | 2025.05.10 |