레벨업 일지

[Java] 백준 15686 치킨 배달 본문

알고리즘/백준

[Java] 백준 15686 치킨 배달

24시간이모자란 2023. 2. 14. 00:37

문제

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

 

15686번: 치킨 배달

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

www.acmicpc.net

알아야 할 개념

  • DFS 백트래킹 

풀이

 

핵심은 치킨집을 (Math.min (M, 치킨집 개수 ) ) 만큼 뽑았을 때 도시 치킨 거리의 수를 모두 구하면 되는 문제.

 

나이브한 브루트포스 풀이 알고리즘은 다음과 같다.

  1. 일정 개수 (min(주어진 M, 치킨집 개수 )) 만큼 치킨집을 뽑는다.
  2. 모든 집의 치킨 거리를 계산한다.
  3. 도시 치킨 거리를 계산한다.
  4. 알고리즘 (1,2,3) 의 모든 경우의 수에 대해 최솟값을 도출한다.

 

 

파이썬엔 컴비네이션이 있지만, 자바는 구현해야한다. (코테 풀때 급할 수록 돌아가라는 뜻인가)

재귀 함수 dfs로 알고리즘(1,2,3) 을 구현했다. 각 Level(재귀 함수 레벨) 에서 치킨집을 하나 리스트에 담은 후 재귀 함수를 호출한다. 호출이 끝난 후에는 현재 Level에서 치킨집 리스트에 담긴 마지막 치킨집을 제거하고 다음 node로 넘어간다.

        for(int i =start  ;i < storeList.size() ;i++){
            selectedStore.add(i);   //하나 추가
            dfs(selectedStore, level+1, i+1,max);//재귀 탐색
            selectedStore.remove(selectedStore.size()-1);   //마지막 리스트에서 빼기
        }

 

이해를 위한 그림

치킨집 개수 3 이고 최대 2개 뽑는 상황일 때 백트래킹 과정이다. 탐색 번호를 1부터 11로 매겼다.

 

각 치킨집을 다 뽑았으면, 도시 치킨 거리를 계산하고 재귀 호출을 더이상 하지 않는다. 

if(level == max){//종료 조건.리프 노트 일때
            int ret =0 ;
                for(int[] home : homeList){
                    int dis = Integer.MAX_VALUE;
                    for(int select: selectedStore){
                        dis = Math.min(
                                Math.abs(home[0]- storeList.get(select)[0]) + Math.abs(home[1]- storeList.get(select)[1])
                                ,dis);
                    }
                    ret += dis;//ret는 도시 치킨 거리
                }
            answer = Math.min(ret, answer);//answer 업데이트
            return;	//재귀호출 더 안함
        }

전체 치킨집 개수보다, 뽑는 것이 많을 때 예외처리를 해주었다. 

dfs(new ArrayList<Integer>(), 0, 0, Math.min(m, storeList.size()));

 

코드

package solve;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    List<int[]> homeList;   // 가정집 리스트
    List<int[]> storeList;  // 치킨집 리스트
    int answer = Integer.MAX_VALUE;     //최솟값 구해야함으로 최댓값으로 초기화

    public static void main(String args[]) throws IOException {
        Main m = new Main();
        System.out.println(m.solve());
    }
    public int solve()throws IOException{
        //입력을 받는다.
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());//최대 치킨집 개수

        int [][] map = new int[n][n];
        homeList = new ArrayList<int[]>();
        storeList = new ArrayList<>();
        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){ //가정집 리스트에 추가
                    homeList.add(new int[]{i, j});//x, y 좌표 추가
                }else if(map[i][j] == 2){
                    storeList.add(new int[]{i,j});
                }
            }
        }

        dfs(new ArrayList<Integer>(), 0, 0, Math.min(m, storeList.size()));
        return answer;
    }

    public void dfs(List<Integer> selectedStore, int level, int start, int max){
        if(level == max){//종료 조건.리프 노트 일때
            int ret =0 ;
                for(int[] home : homeList){
                    int dis = Integer.MAX_VALUE;
                    for(int select: selectedStore){
                        dis = Math.min(
                                Math.abs(home[0]- storeList.get(select)[0]) + Math.abs(home[1]- storeList.get(select)[1])
                                ,dis);
                    }
                    ret += dis;//ret는 도시 치킨 거리
                }
            answer = Math.min(ret, answer);//answer 업데이트
            return;
        }

        for(int i =start  ;i < storeList.size() ;i++){
            selectedStore.add(i);   //하나 추가
            dfs(selectedStore, level+1, i+1,max);//재귀 탐색
            selectedStore.remove(selectedStore.size()-1);   //마지막 리스트에서 빼기
        }
    }
}

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

[Java] 백준 크리보드  (0) 2023.02.20
[Java] 백준 ABCDE  (0) 2023.02.17
[Java] 백준 2839. 설탕 배달  (0) 2023.02.03
[Java] 백준 18405 경쟁적 전염  (0) 2023.01.20
[Java/Python] 백준 11501 주식  (0) 2023.01.19
Comments