2023 한국정보올림피아드(KOI) 1차 대회 2교시 초등부: 두 판 사이의 차이

아두위키 : Arduwiki
(새 문서: {{#seo:|title=아두위키 : 한국정보올림피아드(KOI) 기출문제 풀이|title_mode=append|keywords=정보올림피아드, 한국정보올림피아드, KOI, 정올, 정올 1차대회, 정올 2차대회, 사고력, 자료구조, 컴퓨팅 사고력, 프로그래밍 대회, 정올 기출, 2023 KOI, 2023 초등부 1차 대회 2교시|description=한국정보올림피아드(KOI) 기출 문제 풀이본을 수록합니다.|image=https://arduwiki.com/html/resources/assets/ard...)
 
잔글편집 요약 없음
 
(같은 사용자의 중간 판 12개는 보이지 않습니다)
1번째 줄: 1번째 줄:
{{#seo:|title=아두위키 : 한국정보올림피아드(KOI) 기출문제 풀이|title_mode=append|keywords=정보올림피아드, 한국정보올림피아드, KOI, 정올, 정올 1차대회, 정올 2차대회, 사고력, 자료구조, 컴퓨팅 사고력, 프로그래밍 대회, 정올 기출, 2023 KOI, 2023 초등부 1차 대회 2교시|description=한국정보올림피아드(KOI) 기출 문제 풀이본을 수록합니다.|image=https://arduwiki.com/html/resources/assets/arduwiki.png}}
{{#seo:|title=아두위키 : 한국정보올림피아드(KOI) 기출문제 풀이|title_mode=append|keywords=정보올림피아드, 한국정보올림피아드, KOI, 정올, 정올 1차대회, 정올 2차대회, 사고력, 자료구조, 컴퓨팅 사고력, 프로그래밍 대회, 정올 기출, 2023 KOI, 2023 초등부 1차 대회 2교시, 크림빵, 대피소, 아이템 획득, 정올 크림빵, 정올 대피소, 정올 아이템 획득|description=한국정보올림피아드(KOI) 기출 문제 풀이본을 수록합니다.|image=https://arduwiki.com/html/resources/assets/arduwiki.png}}


'''한국 정보올림피아드(KOI) 기출 문제 풀이과정을 수록합니다.'''
'''한국 정보올림피아드(KOI) 기출 문제 풀이과정을 수록합니다.'''
6번째 줄: 6번째 줄:




== '''[https://codersit.co.kr/oj/problems/1880 1. 등교 (초1)]''' ==
== '''[https://coj.ac/problems/1501 1. 크림빵 (초1)]''' ==
제목 링크를 통해 문제를 확인해주세요.
제목 링크를 통해 문제를 확인해주세요.


=== 📄 문제 개요 ===
=== 📄 문제 개요 ===
매일 아침 학교에 가는 상황을 컴퓨터 프로그램으로 해결해 보는 문제입니다.
KOI 빵집에서 총 N * K개의 빵을 만들었습니다. 이 빵들을 일렬로 늘어놓고 앞에서부터 K개씩 묶어서 판매하려고 합니다.


정올이에게는 여러 대의 '''버스 시간표'''와 학교에 늦지 않게 도착해야 하는 '''기준 시간(X)'''주어집니다.
* 크림이 있는 빵은 1, 크림이 없는 빵은 0으로 표시됩니다.
* 단, 한 묶음 안에 크림이 없는 빵(0)이 P개 이상 있다면 그 묶음은 불량품으로 간주되어 팔 수 없습니다.


우리는 '''판매 가능한 빵 묶음의 총 개수'''를 구해야 합니다.




목표는 다음 두 가지입니다.
=== 💡 문제 풀이 시뮬레이션 ===
이 문제를 해결하기 위한 핵심은 일렬로 늘어선 데이터를 일정한 간격(K)으로 정확하게 나누어 검사하는 것입니다.
 
예를 들어 빵이 3개씩 한 묶음(K=3), 크림 없는 빵이 1개 미만이어야 판매 가능하다고 상상해 봅시다.
 
주어진 빵의 데이터를 '''K개씩 끊어서''' '''크림 없는 빵의 개수'''를 세어보면 판매가능한 빵 묶음 개수를 확인할 수 있습니다.
 
[[파일:2023KOI크림빵1.png|center|class=coders100]]
 
=== 💻 코드 구현 - K개씩 끊어서 확인하기 ===
<syntaxhighlight lang="python3" line="1">
# N: 묶음 수, K: 묶음당 빵의 수, P: 불량품 기준 개수
n, k, p = map(int, input().split())
breads = list(map(int, input().split()))


1. 버스를 타고 학교에 도착하는 시간이 '''기준 시간'''을 넘기지 않아야 합니다.
ans = 0


2. 조건을 만족하는 버스 중 '''가장 늦게 출발하는 버스'''를 선택해야 합니다.
# 0부터 전체 빵 개수(N*K)까지 K칸씩 건너뛰며 묶음의 시작점(i) 설정
for i in range(0, n * k, k):
    nocream = 0  # 현재 묶음의 크림 없는 빵(0) 개수를 셀 변수
   
    # 시작점(i)부터 i+k 직전까지 빵을 하나씩 검사
    for j in range(i, i + k):
        if breads[j] == 0:
            nocream += 1  # 크림이 없으면 개수 증가
           
    # 해당 묶음 안의 불량품이 P개 미만이라면 판매 가능
    if nocream < p:
        ans += 1


만약 어떤 버스를 타도 지각한다면 '''-1을 출력'''합니다.
print(ans)
</syntaxhighlight>




=== 💡 문제 풀이 시뮬레이션 ===
전체 빵을 K개씩 끊어 순회하면서 크림없는 빵의 개수를 세고, 불량 여부를 판단하는 것을 중첩 반복의 형태로 표현했습니다.
코드를 작성하기 전에, 먼저 주어진 상황을 분석해 봅니다.


예를 들어 '''30분''' 안에는 학교에 도착해야 한다고 가정해 보겠습니다.
이번 문제는 '''빵의 최대 개수(1 <= N <= 50)'''가 크지 않기 때문에 이 방법으로 충분히 해결 가능합니다.
{| class="wikitable" style="text-align: center;"
| colspan="1" rowspan="1" |'''버스 번호'''
| colspan="1" rowspan="1" |출발 시간
| colspan="1" rowspan="1" |'''이동 시간'''
| colspan="1" rowspan="1" |'''🏫 학교 도착 시간'''
|-
| colspan="1" rowspan="1" |1번
| colspan="1" rowspan="1" |10분
| colspan="1" rowspan="1" |15분
| colspan="1" rowspan="1" |10 + 15 = 25분
|-
| colspan="1" rowspan="1" |2번
| colspan="1" rowspan="1" |15분
| colspan="1" rowspan="1" |20분
| colspan="1" rowspan="1" |15 + 20 = 35분
|-
| colspan="1" rowspan="1" |3번
| colspan="1" rowspan="1" |20분
| colspan="1" rowspan="1" |10분
| colspan="1" rowspan="1" |20 + 10 = 30분
|}
과정을 차례대로 살펴보면 다음과 같습니다.


1. 각 버스의 도착 시간(출발 시간 + 이동 시간)을 계산합니다.


2. 도착 시간이 30분을 초과하는 2번 버스는 선택지에서 제외합니다.
=== 📊 정답률 ===
[[파일:2023KOI크림빵2.png|center|class=coders70]]


3. 남은 1번, 3번 버스 중 '''가장 늦게 출발'''하는 3번 버스(20분 출발)가 최적의 선택이 됩니다.

== '''[https://coj.ac/problems/1500 2. 대피소 (초2/고1)]''' ==
제목 링크를 통해 문제를 확인해주세요.


=== 📄 문제 개요 ===
KOI 마을에 N개의 집이 있습니다. 이 중 K개의 집에 대피소를 설치하려고 합니다.


복잡한 수학 공식 없이, 도착 시간을 계산하여 지각 여부를 판별하고 통과한 것 중 가장 큰 값을 고르면 됩니다.
모든 주민의 안전을 위해, 어떤 집에서든 '''<nowiki/>'가장 가까운 대피소까지의 거리' 중 가장 먼 거리가 최소가 되도록''' 대피소를 설치해야 합니다.


* '''두 집 사이의 거리'''는 가로 차이와 세로 차이를 합한 값으로 계산합니다.
* 조건을 만족하는 K개의 집을 선택했을 때, '''대피소와 가장 멀리 떨어진 집과의 최소 거리'''를 출력해야 합니다.


=== 💻 코드 구현 - 하나씩 모두 비교하기 ===
<syntaxhighlight lang="python3" line="1">
n, x = map(int, input().split())


ans = -1 # 초기값을 -1로 설정하여 무조건 지각하는 경우를 고려
=== 💡 문제 풀이 시뮬레이션 ===
for i in range(n):
이 문제에서 최적의 위치를 단번에 도출해 내는 수학적 공식이나 모델링을 찾으려 한다면, 오히려 출제 의도에서 벗어날 수 있습니다.
    s, t = map(int, input().split())
    if s + t <= x: # 지각하지 않을 수 있다면
        if ans < s: # 가장 늦게 오는 버스 시간 업데이트
            ans = s


print(ans)
집의 개수(N)는 최대 50개이고, 설치할 대피소(r)는 최대 3개입니다.
</syntaxhighlight>


50개의 집 중에서 3개의 대피소를 고르는 모든 경우의 수는 '''50C3'''으로 고작 '''19,600가지'''에 불과합니다.


=== 📊 정답률 ===
따라서 복잡한 최적화 알고리즘을 구상할 필요 없이, 19,600가지의 대피소 설치 경우의 수를
[[파일:2024KOI등교1.png|center|class=coders70]]


'''모두 확인하는 완전 탐색(Brute-Force)''' 방식으로 접근하는 것이 가장 정확하고 확실한 해결책입니다.


== '''[https://codersit.co.kr/oj/problems/1881 2. 두 배 (초2/중1)]''' ==
[[파일:2023KOI대피소1.png|center|class=coders100]]
제목 링크를 통해 문제를 확인해주세요.
위와 같이 (1, 5), (3, 0), (3, 3), (6, 12), (8, 9) 위치에 총 5개의 집이 있습니다.
이 중 '''(1, 5), (3, 0)''' 두 집을 대피소로 선정한 상황입니다.


=== 📄 문제 개요 ===
수열을 오름차순(점점 커지거나 같은 상태)으로 만들기 위해, 숫자에 '''2를 곱하는 연산'''을 최소 몇 번 해야 하는지 구하는 문제입니다.


* '''상황:''' 앞의 숫자보다 뒤의 숫자가 작다면, 뒤의 숫자에 2를 계속 곱해서 앞의 숫자보다 크거나 같게 만들어야 합니다.
* '''(1, 5) 위치의 집''' : 대피소로 선정되었기 때문에 가장 가까운 대피소까지의 거리는 0입니다.
* '''목표:''' 배열 전체를 오름차순으로 만드는 데 필요한 전체 곱셈 연산의 최소 횟수를 구해야 합니다.
* '''(3, 0) 위치의 집''' : 대피소로 선정되었기 때문에 가장 가까운 대피소까지의 거리는 0입니다.
* '''(3, 3) 위치의 집''' : (1, 5) 대피소까지 거리는 '''4''', (3, 0) 대피소까지 거리는 '''3'''이므로 '''가장 가까운 대피소까지의 거리는 3'''입니다.
* '''(6, 12) 위치의 집''' : (1, 5) 대피소까지 거리는 '''12''', (3, 0) 대피소까지 거리는 '''15'''이므로 '''가장 가까운 대피소까지의 거리는 12'''입니다.
* '''(8, 9) 위치의 집''' : (1, 5) 대피소까지 거리는 '''11''', (3, 0) 대피소까지 거리는 '''14'''이므로 '''가장 가까운 대피소까지의 거리는 11'''입니다.


'''※ 이번 문제는 log 개념을 알고 있다면 더 수월하지만, 초/중등 부문 문제인 만큼 log 사용은 배제합니다.'''





=== 💡 첫 번째 접근 - 직접 2를 곱해보기 ===
따라서 '''가장 가까운 대피소까지의 거리가 가장 먼 경우'''는 (6, 12) 위치의 집의 '''12'''가 됩니다.
가장 먼저 떠오르는 방법은 문제의 조건 그대로 앞의 숫자보다 뒤의 숫자가 작을 때마다 직접 2를 곱해보며 횟수를 세는 것입니다.


[[파일:2024KOI두배1.png|center|class=coders100]]
지금 구한 이 값은 경우의 수 하나의 값이고, 모든 경우의 수에서 계산한 후 가장 작은 값을 구해야 합니다.


* 두 번째 숫자 1은 앞의 3보다 작기 때문에 더 커지기 위해 2를 '''2번''' 곱합니다. '''(현재 누적 연산 2회)'''
* 세 번째 숫자 5는 앞의 4보다 크기 때문에 연산을 하지 않습니다. '''(현재 누적 연산 2회)'''
* 네 번째 숫자 1은 앞의 5보다 작기 때문에 더 커지기 위해 2를 '''3번''' 곱합니다. '''(현재 누적 연산 5회)'''
* 다섯 번째 숫자 5는 앞의 8보다 작기 때문에 더 커지기 위해 2를 '''1번''' 곱합니다. '''(현재 누적 연산 6회)'''



=== 💻 코드 구현 - 직접 2를 곱해보기 ===
<syntaxhighlight lang="python3" line="1">
n = int(input())
arr = list(map(int, input().split()))


ans = 0
풀이과정을 정리해보면 다음과 같습니다.


for i in range(1, n):
① 주어진 N개의 집 중 r개를 대피소로 정합니다.
    while arr[i-1] > arr[i]: # 내 앞 숫자보다 커질 때까지 2를 곱하기
        arr[i] *= 2
        ans += 1


print(ans)
② 각 집과 대피소 사이 가장 가까운 거리(p)를 구한 후, 그 중 가장 큰 값(k)을 선정합니다.
</syntaxhighlight>이 코드로도 주어진 예제들에 대한 정답은 잘 출력되는 것을 볼 수 있습니다.
하지만 수열의 길이(N)가 최대 250,000개일 때, '''숫자가 계속 작아지는 최악의 경우'''를 상상해 봅시다.


마지막 숫자는 2를 수십만 번 곱해야 하고, 숫자의 길이는 수만 자리를 넘어가게 됩니다.
③ 모든 경우에 대해 위 과정을 반복하여 k 중 가장 작은 값(m)을 구합니다.


이처럼 거대한 숫자를 매번 비교하고 곱하는 과정에서 엄청난 연산 시간이 소모되어 결국 '''시간 초과'''가 발생합니다.


그래서 위 코드로는 '''33점''' 획득이 가능합니다.
=== 💻 코드 구현 ===
<syntaxhighlight lang="python3" line="1">
n, r = map(int, input().split())
arr = []
for i in range(n):
    arr.append(tuple(map(int, input().split())))


n = len(arr)
data = []        # 현재 선택된 대피소들을 담을 리스트
m = float('inf')  # 최종 정답 (가장 작은 값을 찾기 위해 무한대로 초기화)


=== 💡 두 번째 접근 - 곱한 횟수 기록하기 ===
# 조합을 직접 만들어내는 재귀함수 (start: 탐색 시작 인덱스, depth: 선택한 대피소 개수)
이번에는 숫자를 직접 부풀리는 대신, 이 숫자는 2를 몇 번 곱한 상태인지 '''횟수'''를 따로 기록해 두는 방법입니다.
def comb(start, depth):
    global m
   
    # r개의 대피소를 모두 골랐다면
    if depth == r:
        k = 0  # 현재 대피소 조합에서 가장 멀리 떨어진 집의 거리
       
        # 마을의 모든 집(i)에 대해 검사
        for i in arr:
            p = float('inf')  # 현재 집(i)에서 가장 가까운 대피소까지의 거리
           
            # 선택된 대피소(j)들을 하나씩 확인하며 최단 거리 계산
            for j in data:
                p = min(p, abs(i[0]-j[0]) + abs(i[1]-j[1]))
           
            # 모든 집들 중 가장 대피소와 멀리 떨어진 거리(최댓값) 갱신
            k = max(k, p)
           
        # 이번 조합의 최댓값(k)이 기존의 최소 거리(m)보다 작다면 정답 갱신
        m = min(m, k)
        return


현재 숫자와 이전 숫자의 '''원래 크기'''를 비교하여 다음과 같이 횟수(cnt)를 정합니다.
    # 대피소 고르기
    for i in range(start, n):
        data.append(arr[i])      # 1. 대피소 선택
        comb(i + 1, depth + 1)  # 2. 다음 대피소 고르러 재귀 호출
        data.pop()              # 3. 탐색 완료 후 빼내고 다른 집 선택


* '''이전 숫자가 더 클 때 (따라잡아야 할 때)'''
comb(0, 0)
print(m)
</syntaxhighlight>



<nowiki>:</nowiki> 현재 숫자에 2를 몇 번 곱해야 이전 숫자 이상이 되는지 계산하여, 그 횟수만큼 이전 숫자의 누적 횟수에 더해줍니다.
'''1. 알고리즘 문제 풀이 요령'''


* '''현재 숫자가 더 클 (여유가 있을 때)'''
이번 문제 뿐만 아니라 알고리즘을 설계할 가장 먼저 확인할 것이 입력 데이터의 크기와 제한시간입니다.


<nowiki>:</nowiki> 현재 숫자가 이미 크기 때문에, 이전 숫자의 누적 횟수에서 2를 나눌 수 있는 만큼(여유분) 횟수를 차감해 줍니다.
집의 개수(N)의 최대값이 50으로 굉장히 작은데도 불구하고, 문제의 시간제한이 4초로 아주 크게 주어졌습니다.


[[파일:2024KOI두배2.png|center|class=coders100]]
'''입력 데이터의 크기와 시간 제한'''은 이렇게 '''완전 탐색을 암시하는 힌트'''가 될 수 있습니다.


알고리즘 설계 방향에 이를 반영하되, 문제에 따라 예외 상황이 있을 수 있기 때문에 올바른 판단을 위해 많은 문제 풀이 경험이 필요하겠습니다.


=== 💻 코드 구현 - 곱한 횟수 기록하기 ===
<syntaxhighlight lang="python3" line="1">
import sys
input = sys.stdin.readline


n = int(input())
a = list(map(int, input().split()))


ans = 0
'''2. 재귀함수를 활용한 조합 구현'''
cnt = 0


for i in range(1, n):
combinations  라이브러리를 활용할 수도 있지만, 재귀함수를 활용해 직접 조합을 구현했습니다.
    prev, curr = a[i-1], a[i]
   
    if prev > curr: # 이전 숫자가 더 크다면
        k = 0
        tmp = curr
        while tmp < prev: # 2를 곱하는 횟수 누적
            tmp *= 2
            k += 1
        cnt += k
       
    else: # 현재 숫자가 더 크다면
        k = 0
        tmp = prev
        while tmp * 2 <= curr: # 여유 횟수만큼 누적
            tmp *= 2
            k += 1
        cnt = max(0, cnt - k) # 이전 누적 횟수에서 여유분 차감(cnt - k), 0보다 작아질 수는 없음
       
    ans += cnt


print(ans)
조합 라이브러리가 구현되어있지 않는 프로그래밍 언어를 사용하거나 라이브러리 사용이 제한적인 상황에서 문제를 잘 풀어내려면
</syntaxhighlight>숫자를 직접 곱하지 않고 원래 숫자들 간의 '''스케일 차이만 비교'''하기 때문에 20만 개의 숫자가 주어져도 무리없이 '''100점을''' 받을 수 있습니다.


'''순열/조합'''과 같은 중요한 개념은 이렇게 직접 구현하는 연습도 해보면 좋겠습니다.


=== 📊 정답률 ===
[[파일:2024KOI두배3.png|center|class=coders70]]


[[파일:2024KOI두배4.png|center|class=coders70]]
=== 📊 정답률 ===
[[파일:2023KOI대피소2.png|center|class=coders70]]


=== '''''' ===
[[파일:2023KOI대피소3.png|center|class=coders70]]


== '''[https://codersit.co.kr/oj/problems/1882 3. 반품 회수 (초3/고1)]''' ==
== '''3. 아이템 획득 (초3)''' ==
추가 예정
추가 예정

2026년 5월 29일 (금) 22:54 기준 최신판


한국 정보올림피아드(KOI) 기출 문제 풀이과정을 수록합니다.

한국정보올림피아드(KOI) 기출 문제 풀이 모음
한국정보올림피아드(KOI) 기출 문제 풀이 모음


1. 크림빵 (초1)

제목 링크를 통해 문제를 확인해주세요.

📄 문제 개요

KOI 빵집에서 총 N * K개의 빵을 만들었습니다. 이 빵들을 일렬로 늘어놓고 앞에서부터 K개씩 묶어서 판매하려고 합니다.

  • 크림이 있는 빵은 1, 크림이 없는 빵은 0으로 표시됩니다.
  • 단, 한 묶음 안에 크림이 없는 빵(0)이 P개 이상 있다면 그 묶음은 불량품으로 간주되어 팔 수 없습니다.

우리는 판매 가능한 빵 묶음의 총 개수를 구해야 합니다.


💡 문제 풀이 시뮬레이션

이 문제를 해결하기 위한 핵심은 일렬로 늘어선 데이터를 일정한 간격(K)으로 정확하게 나누어 검사하는 것입니다.

예를 들어 빵이 3개씩 한 묶음(K=3), 크림 없는 빵이 1개 미만이어야 판매 가능하다고 상상해 봅시다.

주어진 빵의 데이터를 K개씩 끊어서 크림 없는 빵의 개수를 세어보면 판매가능한 빵 묶음 개수를 확인할 수 있습니다.



💻 코드 구현 - K개씩 끊어서 확인하기

# N: 묶음 수, K: 묶음당 빵의 수, P: 불량품 기준 개수
n, k, p = map(int, input().split())
breads = list(map(int, input().split()))

ans = 0

# 0부터 전체 빵 개수(N*K)까지 K칸씩 건너뛰며 묶음의 시작점(i) 설정
for i in range(0, n * k, k):
    nocream = 0  # 현재 묶음의 크림 없는 빵(0) 개수를 셀 변수
    
    # 시작점(i)부터 i+k 직전까지 빵을 하나씩 검사
    for j in range(i, i + k):
        if breads[j] == 0:
            nocream += 1  # 크림이 없으면 개수 증가
            
    # 해당 묶음 안의 불량품이 P개 미만이라면 판매 가능
    if nocream < p:
        ans += 1

print(ans)


전체 빵을 K개씩 끊어 순회하면서 크림없는 빵의 개수를 세고, 불량 여부를 판단하는 것을 중첩 반복의 형태로 표현했습니다.

이번 문제는 빵의 최대 개수(1 <= N <= 50)가 크지 않기 때문에 이 방법으로 충분히 해결 가능합니다.


📊 정답률



2. 대피소 (초2/고1)

제목 링크를 통해 문제를 확인해주세요.

📄 문제 개요

KOI 마을에 N개의 집이 있습니다. 이 중 K개의 집에 대피소를 설치하려고 합니다.

모든 주민의 안전을 위해, 어떤 집에서든 '가장 가까운 대피소까지의 거리' 중 가장 먼 거리가 최소가 되도록 대피소를 설치해야 합니다.

  • 두 집 사이의 거리는 가로 차이와 세로 차이를 합한 값으로 계산합니다.
  • 조건을 만족하는 K개의 집을 선택했을 때, 대피소와 가장 멀리 떨어진 집과의 최소 거리를 출력해야 합니다.


💡 문제 풀이 시뮬레이션

이 문제에서 최적의 위치를 단번에 도출해 내는 수학적 공식이나 모델링을 찾으려 한다면, 오히려 출제 의도에서 벗어날 수 있습니다.

집의 개수(N)는 최대 50개이고, 설치할 대피소(r)는 최대 3개입니다.

50개의 집 중에서 3개의 대피소를 고르는 모든 경우의 수는 50C3으로 고작 19,600가지에 불과합니다.

따라서 복잡한 최적화 알고리즘을 구상할 필요 없이, 19,600가지의 대피소 설치 경우의 수를

모두 확인하는 완전 탐색(Brute-Force) 방식으로 접근하는 것이 가장 정확하고 확실한 해결책입니다.



위와 같이 (1, 5), (3, 0), (3, 3), (6, 12), (8, 9) 위치에 총 5개의 집이 있습니다. 이 중 (1, 5), (3, 0) 두 집을 대피소로 선정한 상황입니다.


  • (1, 5) 위치의 집 : 대피소로 선정되었기 때문에 가장 가까운 대피소까지의 거리는 0입니다.
  • (3, 0) 위치의 집 : 대피소로 선정되었기 때문에 가장 가까운 대피소까지의 거리는 0입니다.
  • (3, 3) 위치의 집 : (1, 5) 대피소까지 거리는 4, (3, 0) 대피소까지 거리는 3이므로 가장 가까운 대피소까지의 거리는 3입니다.
  • (6, 12) 위치의 집 : (1, 5) 대피소까지 거리는 12, (3, 0) 대피소까지 거리는 15이므로 가장 가까운 대피소까지의 거리는 12입니다.
  • (8, 9) 위치의 집 : (1, 5) 대피소까지 거리는 11, (3, 0) 대피소까지 거리는 14이므로 가장 가까운 대피소까지의 거리는 11입니다.


따라서 가장 가까운 대피소까지의 거리가 가장 먼 경우는 (6, 12) 위치의 집의 12가 됩니다.

지금 구한 이 값은 경우의 수 하나의 값이고, 모든 경우의 수에서 계산한 후 가장 작은 값을 구해야 합니다.


풀이과정을 정리해보면 다음과 같습니다.

① 주어진 N개의 집 중 r개를 대피소로 정합니다.

② 각 집과 대피소 사이 가장 가까운 거리(p)를 구한 후, 그 중 가장 큰 값(k)을 선정합니다.

③ 모든 경우에 대해 위 과정을 반복하여 k 중 가장 작은 값(m)을 구합니다.


💻 코드 구현



n, r = map(int, input().split())
arr = []
for i in range(n):
    arr.append(tuple(map(int, input().split())))

n = len(arr)
data = []         # 현재 선택된 대피소들을 담을 리스트
m = float('inf')  # 최종 정답 (가장 작은 값을 찾기 위해 무한대로 초기화)

# 조합을 직접 만들어내는 재귀함수 (start: 탐색 시작 인덱스, depth: 선택한 대피소 개수)
def comb(start, depth):
    global m
    
    # r개의 대피소를 모두 골랐다면
    if depth == r:
        k = 0  # 현재 대피소 조합에서 가장 멀리 떨어진 집의 거리
        
        # 마을의 모든 집(i)에 대해 검사
        for i in arr:
            p = float('inf')  # 현재 집(i)에서 가장 가까운 대피소까지의 거리
            
            # 선택된 대피소(j)들을 하나씩 확인하며 최단 거리 계산
            for j in data:
                p = min(p, abs(i[0]-j[0]) + abs(i[1]-j[1]))
            
            # 모든 집들 중 가장 대피소와 멀리 떨어진 거리(최댓값) 갱신
            k = max(k, p)
            
        # 이번 조합의 최댓값(k)이 기존의 최소 거리(m)보다 작다면 정답 갱신
        m = min(m, k)
        return

    # 대피소 고르기
    for i in range(start, n):
        data.append(arr[i])      # 1. 대피소 선택
        comb(i + 1, depth + 1)   # 2. 다음 대피소 고르러 재귀 호출
        data.pop()               # 3. 탐색 완료 후 빼내고 다른 집 선택

comb(0, 0)
print(m)



1. 알고리즘 문제 풀이 요령

이번 문제 뿐만 아니라 알고리즘을 설계할 때 가장 먼저 확인할 것이 입력 데이터의 크기와 제한시간입니다.

집의 개수(N)의 최대값이 50으로 굉장히 작은데도 불구하고, 문제의 시간제한이 4초로 아주 크게 주어졌습니다.

입력 데이터의 크기와 시간 제한은 이렇게 완전 탐색을 암시하는 힌트가 될 수 있습니다.

알고리즘 설계 방향에 이를 반영하되, 문제에 따라 예외 상황이 있을 수 있기 때문에 올바른 판단을 위해 많은 문제 풀이 경험이 필요하겠습니다.


2. 재귀함수를 활용한 조합 구현

combinations 라이브러리를 활용할 수도 있지만, 재귀함수를 활용해 직접 조합을 구현했습니다.

조합 라이브러리가 구현되어있지 않는 프로그래밍 언어를 사용하거나 라이브러리 사용이 제한적인 상황에서 문제를 잘 풀어내려면

순열/조합과 같은 중요한 개념은 이렇게 직접 구현하는 연습도 해보면 좋겠습니다.


📊 정답률

3. 아이템 획득 (초3)

추가 예정