Algorithm/Algorithm Problem

백준 18513번: 샘터 (Python/Java)

주유소짜글이 2022. 1. 11. 22:03

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

 

18513번: 샘터

첫째 줄에 자연수 N과 K가 공백을 기준으로 구분되어 주어진다. (1 ≤ N, K ≤ 100,000) 둘째 줄에 N개의 샘터의 위치가 공백을 기준으로 구분되어 정수 형태로 주어진다. (-100,000,000 ≤ 샘터의 위치 ≤

www.acmicpc.net

 

 

 

접근


이 문제에서 물어보는 것은 불행도의 합이 가장 적은 방법으로 집을 위치시키고, 불행도를 구하라는 것이다. 문제만 읽어봐도 알 수 있듯이 말만 불행도라고 했지, 결국 샘터와 가장 가까운 집들의 거리합을 구하는 문제다.(애초에 문제에서 집의 불행도는 샘터와의 거리로 정의된다고 적어놨다) 가장 가까운 거리의 합을 구하는 전형적인 너비우선탐색 문제라고 볼 수 있다.

 

그렇다면 가장 가까운 거리의 합은 어떻게 구할까? 탐색에 사용할 큐에 모든 샘터들의 위치를 넣는다. 그리고 하나씩 꺼내 양 옆에 샘터, 혹은 집이 있는지 살펴보고 있다면 넘어가고 없다면 위치를 체크한 뒤 해당 위치를 큐에 넣어준다. 이를 반복하다 보면 구해야 하는 집의 수만큼 체크할 수 있을 것이고 그때 탐색을 끝낸 뒤, 거리 합을 출력하면 된다.

 

여기서 주의할 것이 샘터의 범위 조건이다. 샘터의 범위는 -100,000,000과 100,000,000 사이라고 나와있다. 다시 한번 말하지만 이는 샘터의 범위 조건이다. 집은 이 범위 밖을 벗어나도 상관없다. 이 문제는 집 위치 범위가 따로 없기 때문에 범위는 신경 쓰지 않아도 된다. 이 부분만 짚고 넘어가면 어렵지 않게 답을 맞힐 수 있다.

 

 

 

풀이


Python

from collections import deque

waterNum, houseNum = map(int, (input()).split())
waterList = list(map(int, (input()).split()))

visitList = {}
bfsDeque = deque()
for i in range(waterNum):
    visitList[waterList[i]] = True
    bfsDeque.append([waterList[i], 0])

leftRight = [-1, 1]

result = 0
thisHouse = 0
while True:
    thisDeque = bfsDeque.popleft()

    for i in range(2):
        subIdx = (thisDeque[0] + leftRight[i])

        if not subIdx in visitList:
            visitList[subIdx] = True
            bfsDeque.append([subIdx, (thisDeque[1]+1)])
            thisHouse += 1

            if thisHouse <= houseNum:
                result += (thisDeque[1] + 1)
            else:
                break
    
    if thisHouse >= houseNum:
        break

print(result)

 

Java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

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

        String[] firstLine = (br.readLine()).split(" ");
        int waterNum = Integer.parseInt(firstLine[0]);
        int houseNum = Integer.parseInt(firstLine[1]);
        
        String[] secondLine = (br.readLine()).split(" ");
        HashMap<Integer, Boolean> visitList = new HashMap<Integer, Boolean>();
        Queue<int[]> bfsQueue = new LinkedList<>();
        for(int i = 0 ; i < waterNum ; i++){
            visitList.put(Integer.parseInt(secondLine[i]), true);
            bfsQueue.add(new int[] {Integer.parseInt(secondLine[i]), 0});
        }

        int[] leftRight = {-1, 1};

        long result = 0;
        int thisHouse = 0;
        loop:
        while(true){
            int[] thisQueue = bfsQueue.poll();

            for(int i = 0 ; i < 2 ; i++){
                int subIdx = (thisQueue[0] + leftRight[i]);

                if(!visitList.containsKey(subIdx)){
                    visitList.put(subIdx, true);
                    bfsQueue.add(new int[] {subIdx, (thisQueue[1]+1)});
                    thisHouse++;

                    if(thisHouse <= houseNum){
                        result += (thisQueue[1] + 1);
                    }else{
                        break loop;
                    }
                }
            }
        }

        System.out.println(result);

    }

}

 

 

 

후기


요 근래 일주일 하고도 며칠 그리디 문제만 풀었더니 너비우선탐색이 땡겨 풀어봤다. 구현이 조금 복잡할 수는 있어도 역시 그래프 문제가 재밌긴 한 것 같다.

 

한 가지 찜찜한 부분이 있는데, 탐색 영역 체크 구현이다. 범위 조건이 따로 없는 문제다 보니 따로 배열을 만드는 방법 같은 것들은 사용할 수 없었다. 그렇다고 체크를 안 할 수는 없어 생각해낸 부분이 파이썬의 경우엔 dict를, 자바의 경우엔 hashmap을 사용한 것이었다. 탐색한 위치의 인덱스를 키 값으로 설정해 추가해주고 해당 키 값이 dict 혹은 hashmap에 있는지 없는지를 확인해 탐색 여부를 체크했다. 어쨌든 정답 처리가 되긴 했지만 이렇게 풀어도 되는지, 조금 더 좋은 방법이 있는지 생각해봐야겠다.