<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>명이나물 라이브러리</title>
    <link>https://mymelody.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 19 Jul 2026 22:03:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>명이나물 라이브러리</managingEditor>
    <image>
      <title>명이나물 라이브러리</title>
      <url>https://tistory1.daumcdn.net/tistory/4785548/attach/fe0237ef23e142299dfe46b250b00ec9</url>
      <link>https://mymelody.tistory.com</link>
    </image>
    <item>
      <title>programmers) 입국심사</title>
      <link>https://mymelody.tistory.com/353</link>
      <description>&lt;h1&gt;프로그래머스 입국심사, 사람배정이 아닌 시간을 이분 탐색하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 프로그래머스의 &lt;b&gt;입국심사&lt;/b&gt; 문제를 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;프로그래머스 입국심사 문제&quot; href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43238&quot;&gt; 프로그래머스&amp;nbsp;입국심사&amp;nbsp;문제&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 꽤 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 읽었을 때 자연스럽게 이렇게 생각했다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;각 사람을 어떤 심사관에게 배정해야 하지?
더 빠른 심사관에게 먼저 보내야 하나?
비어 있는 심사대가 있어도 기다렸다가 더 빨리 끝나는 심사대를 이용해야 하나?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 사람을 심사관에게 직접 배정하는 문제가 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;u&gt;&lt;b&gt;n명을 모두 심사할 수 있는 최소 시간&lt;/b&gt;&lt;/u&gt;을 구하는 것이었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입국심사를 기다리는 사람 수 n명이 있고, 각 심사관이 한 명을 심사하는 데 걸리는 시간이 times 배열로 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 사람이 심사를 받는 데 걸리는 최소 시간을 구해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;n = 6;
times = [7, 10];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7분이 걸리는 심사관과 10분이 걸리는 심사관이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6명을 모두 심사하는 최소 시간은 28분이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;문제 유형 분류&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;이분 탐색&lt;/b&gt; 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히는 정렬된 배열에서 특정 값을 찾는 일반적인 이분 탐색이 아니라,&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;정답이 될 수 있는 시간 범위를 이분 탐색하는 문제
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제는 보통 아래와 같은 특징이 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;최소 시간을 구하라
최대 길이를 구하라
최소 비용을 구하라
어떤 값이 가능하거나 불가능한지 판단할 수 있다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입국심사에서는 다음 질문으로 바꿀 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;X분 안에 n명을 모두 심사할 수 있는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 답할 수 있기 때문에 시간을 기준으로 이분 탐색을 할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;처음 접근했던 방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 사람을 심사관에게 직접 배정하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1번 사람은 7분 심사관
2번 사람은 10분 심사관
3번 사람은 다시 7분 심사관
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 times = [7, 10]이면 각각의 심사 종료 시간 배열을 만들어야 하나 고민했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;7분 심사관 종료 시간
7, 14, 21, 28, 35, 42 ...

10분 심사관 종료 시간
10, 20, 30, 40, 50, 60 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 배열을 합치면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;7, 10, 14, 20, 21, 28, 30 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6번째 사람이 끝나는 시간은 28분이므로 정답이 28이라는 것은 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 n이 매우 커지면 종료 시간을 하나씩 배열에 저장하는 방식은 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 문제는 종료 시간을 직접 나열하거나 사람을 배정하는 방식으로 풀면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;핵심 사고 전환&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 가장 중요한 것은 아래처럼 생각을 바꾸는 것이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사람을 심사관에게 어떻게 배정할까?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 아니라,&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;mid분 안에 각 심사관은 몇 명을 처리할 수 있을까?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 계산해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 mid = 28이라고 해보자.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;7분 심사관
28 / 7 = 4명

10분 심사관
28 / 10 = 2명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 총 처리 가능한 사람 수는:&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;4 + 2 = 6명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 28분 안에 6명을 심사할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 누가 어떤 심사관에게 가는지 직접 배정하지 않았다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 각 심사관이 28분 동안 처리할 수 있는 인원만 계산했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;비어 있는 심사대가 있어도 기다릴 수 있다는 조건&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 설명에는 비어 있는 심사대가 있어도 더 빨리 끝나는 심사대를 기다릴 수 있다는 내용이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 처음에 많이 헷갈렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 times = [7, 10], n = 6일 때를 생각해보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;7분 심사관
0~7
7~14
14~21
21~28

10분 심사관
0~10
10~20
20~30
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;20분에 10분 심사대가 비어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 마지막 사람이 바로 10분 심사대에 들어가면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;20~30
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 되어 전체 완료 시간이 30분이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 1분을 기다렸다가 21분에 비는 7분 심사대에 들어가면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;21~28
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 되어 전체 완료 시간이 28분이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 빈 심사대가 있다고 무조건 바로 이용하는 것이 최적은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 대기 여부를 직접 코드로 구현할 필요는 없다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;28 / 7 = 4명
28 / 10 = 2명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 계산 안에는 마지막 사람이 21분까지 기다렸다가 7분 심사대를 이용하는 경우까지 이미 포함되어 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;시간 범위 정하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색을 하려면 먼저 시간 범위를 정해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최소 시간&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 작은 시간 후보는 1분으로 둘 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;long left = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답이 0분일 수는 없기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최대 시간&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 시간은 가장 빠른 심사관이 모든 사람을 혼자 심사하는 경우로 잡을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;Arrays.sort(times);

long right = (long) times[0] * n;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;n = 6;
times = [7, 10];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이면 정렬 후 가장 빠른 심사 시간은 7분이다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;7분 심사관이 혼자 6명을 심사하면
7 * 6 = 42분
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정답은 무조건 1분부터 42분 사이에 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = 1
right = 42
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;mid 시간으로 가능한지 확인하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색에서는 현재 범위의 중간 시간을 구한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;long mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 mid는 다음 질문의 기준 시간이다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;mid분 안에 n명을 모두 심사할 수 있는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 심사관이 mid분 동안 처리할 수 있는 사람 수를 모두 더한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;long student = 0;

for (int time : times) {
    student += mid / time;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 mid = 21이면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;21 / 7 = 3명
21 / 10 = 2명

총 5명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6명보다 적으므로 21분은 부족하다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;student &amp;lt; n
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 시간을 더 늘려야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 mid = 32이면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;32 / 7 = 4명
32 / 10 = 3명

총 7명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6명 이상 심사할 수 있으므로 32분은 가능하다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;student &amp;gt;= n
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리는 최소 시간을 찾아야 하므로 더 작은 시간도 가능한지 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;min = mid;
right = mid - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;내가 최종적으로 작성한 코드&lt;/h1&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public long solution(int n, int[] times) {
        Arrays.sort(times);

        long left = 1;
        long right = (long) times[0] * n;
        long min = Long.MAX_VALUE;

        while (right &amp;gt;= left) {
            long student = 0;
            long mid = left + (right - left) / 2;

            for (int time : times) {
                student += (mid / time);
            }

            if (student &amp;gt;= n) {
                right = mid - 1;
                min = mid;
            } else if (student &amp;lt; n) {
                left = mid + 1;
            }
        }

        return min;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;코드 흐름 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;1. 심사 시간을 정렬한다.
2. left는 1분으로 설정한다.
3. right는 가장 빠른 심사관이 n명을 모두 심사하는 시간으로 설정한다.
4. mid 시간을 구한다.
5. mid분 동안 처리 가능한 사람 수를 계산한다.
6. n명 이상 처리 가능하면:
   - 현재 mid는 정답 후보
   - 더 작은 시간을 확인하기 위해 right를 줄인다.
7. n명보다 적게 처리 가능하면:
   - 시간이 부족하므로 left를 늘린다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;처음에 실수했던 부분&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 사람을 직접 배정하려고 했던 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 사람을 어떤 심사관에게 보내야 하는지 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 배정 문제가 아니라 시간에 대한 가능 여부를 판단하는 문제였다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;X분 안에 n명을 심사할 수 있는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문으로 바꾸는 것이 핵심이었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 종료 시간 배열을 만들려고 했던 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 7의 배수, 10의 배수처럼 각 심사관의 종료 시간을 배열로 만들고 비교하려고 했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;7, 14, 21, 28 ...
10, 20, 30, 40 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 예제에서는 가능해 보이지만, n이 크면 종료 시간 배열을 전부 만들 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 직접 나열하지 않고 아래처럼 계산해야 한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;student += mid / time;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. mid 계산식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 괄호 위치를 잘못 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 코드:&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;mid = (left + (right - left)) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 식은 정리하면 사실상 아래와 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;mid = right / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 left와 right의 가운데를 구하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바른 코드는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. student를 반복마다 초기화해야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;student는 현재 mid 시간 안에 처리 가능한 사람 수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 mid가 바뀔 때마다 다시 0부터 계산해야 한다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;while (right &amp;gt;= left) {
    long student = 0;
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 student를 while문 밖에 두면 이전 mid에서 계산한 값이 계속 누적되어 잘못된 결과가 나온다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;시간 복잡도&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심사 시간을 정렬하는 데:&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;O(T log T)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 시간 범위를 절반씩 줄이므로:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;O(log(maxTime * n))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 이분 탐색 단계마다 모든 심사관을 확인하므로:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(T)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;O(T log T + T log(maxTime * n))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 T는 심사관 수이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;공간 복잡도&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 배열이나 리스트를 만들지 않고 변수만 사용했다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;공간복잡도: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 내부에서 사용하는 공간은 제외하고 생각했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;이번 문제에서 배운 점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 이분 탐색이 정렬된 배열에서만 사용하는 알고리즘이 아니라는 것을 알려준 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답이 될 수 있는 시간 범위를 줄여나가면서 최소값을 찾을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 비슷한 문제를 만나면 아래 질문을 먼저 해봐야겠다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;구해야 하는 값이 숫자인가?
최소값 또는 최대값을 구하는가?
어떤 값 X를 정했을 때 가능/불가능을 판단할 수 있는가?
X가 커질수록 가능해지는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입국심사 문제는 모두 만족한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;구해야 하는 값: 최소 시간
판단 기준: X분 안에 n명 심사 가능 여부
시간이 커질수록: 처리 가능한 사람 수 증가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이분 탐색으로 풀 수 있었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;셀프 체크&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 못 풀었는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람을 심사관에게 직접 배정하는 문제라고 생각해서 접근이 복잡해졌다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간이 부족했는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간보다 문제를 보는 관점이 바로 떠오르지 않았던 것이 더 컸다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 해석을 잘못했는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 자체는 이해했지만, 구해야 하는 값이 &amp;ldquo;n명을 심사할 수 있는 최소 시간&amp;rdquo;이라는 점에서 출발하지 못했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 각 심사관의 종료 시간을 배열로 만들려고 했지만, 입력이 커지면 사용할 수 없는 방식이었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현 실수가 있었는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mid 계산식의 괄호 위치를 잘못 작성했고, student를 반복마다 초기화해야 한다는 점도 놓쳤다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 판단을 잘못했는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간을 하나씩 나열하는 방식은 n이 커지면 너무 많은 시간과 공간이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색을 사용하면 시간 범위를 절반씩 줄여가며 최소 시간을 구할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;마무리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입국심사 문제의 핵심은 이 한 문장으로 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;사람을 심사관에게 직접 배정하지 말고,
X분 안에 몇 명을 심사할 수 있는지 계산하자.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 통해 &amp;ldquo;정답을 이분 탐색하는 문제&amp;rdquo;의 사고방식을 조금 더 이해할 수 있었다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/353</guid>
      <comments>https://mymelody.tistory.com/353#entry353comment</comments>
      <pubDate>Tue, 30 Jun 2026 22:40:26 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 체육복 | 두번째풀이</title>
      <link>https://mymelody.tistory.com/352</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42862&quot;&gt; 프로그래머스 체육복 문제&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 학생 수 n명이 있고, 일부 학생은 체육복을 도난당했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 일부 학생은 여벌 체육복을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복을 도난당한 학생은 바로 앞번호나 바로 뒷번호 학생에게만 체육복을 빌릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, i번 학생은 아래 학생에게만 빌릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;i - 1번 학생
i + 1번 학생
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복을 빌려줄 수 있는 학생은 여벌 체육복을 가진 학생뿐이고, 여벌 체육복은 한 벌만 빌려줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 체육 수업을 들을 수 있는 학생의 최댓값을 구해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;그리디&lt;/b&gt; 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복을 도난당한 학생을 앞번호부터 확인하면서, 빌릴 수 있는 학생이 있으면 바로 빌리는 방식으로 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 중요한 점은 &lt;b&gt;앞번호부터 차례대로 처리해야 한다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 도난당한 학생이 i라면 다음 순서로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. i - 1번 학생이 여벌 체육복을 가지고 있는지 확인한다.
2. 없다면 i + 1번 학생이 여벌 체육복을 가지고 있는지 확인한다.
3. 빌렸다면 해당 여벌 체육복은 다시 사용할 수 없게 처리한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 범위 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 번호는 1번부터 n번까지 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lost 배열에는 체육복을 도난당한 학생 번호가 들어 있고,&lt;br /&gt;reserve 배열에는 여벌 체육복이 있는 학생 번호가 들어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 주의해야 할 점은 lost와 reserve에 같은 학생이 동시에 들어 있을 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 경우이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lost = [2, 4]
reserve = [2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 학생은 체육복을 도난당했지만, 여벌 체육복도 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 2번 학생은 자기 체육복을 입을 수는 있지만, 다른 학생에게 빌려줄 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이런 학생은 먼저 lost와 reserve 양쪽에서 제외해줘야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 접근&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 체육복이 없는 학생 목록을 따로 만들고, 여벌이 있는 학생을 따로 표시하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이런 식으로 생각했다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;체육복 없는 학생 목록 만들기
여벌 체육복 있는 학생 표시하기
없는 학생이 앞뒤 학생에게 빌릴 수 있는지 확인하기
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향 자체는 나쁘지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 처음 코드에서는 이중 반복문 안에서 체육복 없는 학생을 리스트에 추가하다 보니, 같은 학생이 여러 번 들어가는 문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 리스트를 순회하면서 remove()를 사용하려고 하면서 인덱스와 학생 번호가 헷갈리는 문제도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 lost와 reserve 배열을 정렬한 뒤, 이미 처리한 학생을 -1로 표시하는 방식으로 수정했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 예외 처리: 도난당했지만 여벌도 있는 학생&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 처리해야 하는 것은 lost와 reserve에 동시에 있는 학생이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 학생은 자기 체육복 문제는 해결할 수 있지만, 다른 학생에게 빌려줄 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 먼저 두 배열에서 해당 학생을 -1로 표시해 제외했다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; lost.length; i++) { 
    for (int j = 0; j &amp;lt; reserve.length; j++) {
        if (lost[i] == reserve[j]) {
            lost[i] = -1;
            reserve[j] = -1;
            answer++;                    
            break;
        } 
    } 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 answer는 체육 수업을 들을 수 있는 학생 수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 체육복을 도난당한 학생 수만큼 제외해서 시작했다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;int answer = n - lost.length;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 도난당했지만 여벌도 있는 학생은 결국 수업을 들을 수 있으므로 answer++를 해주었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;학생 번호와 배열 인덱스가 헷갈렸던 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서 가장 헷갈렸던 부분은 배열의 원소가 학생 번호라는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 배열이 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lost = [2, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 i는 배열 인덱스이고, lost[i]는 학생 번호이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;i = 0, lost[i] = 2
i = 1, lost[i] = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 학생 번호와 배열 인덱스를 헷갈려서 아래처럼 접근하려고 했다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;lost[less] = -1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 less는 학생 번호이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 less = 4라면 lost[4]에 접근하게 되는데, lost 배열의 길이가 2라면 인덱스 범위를 벗어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 배열 값을 수정할 때는 반드시 인덱스 i, j를 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;lost[i] = -1;
reserve[j] = -1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서 꼭 기억해야 할 점은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;i, j는 배열 인덱스
lost[i], reserve[j]는 학생 번호
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;체육복 빌려주기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겹치는 학생을 먼저 처리한 뒤, 남은 도난 학생을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 체육복이 없는 학생이라면, 여벌 체육복을 가진 학생 중 번호 차이가 1인 학생을 찾는다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; lost.length; i++) {
    if (lost[i] == -1) continue;

    for (int j = 0; j &amp;lt; reserve.length; j++) {
        if (reserve[j] == -1) continue;

        if (Math.abs(lost[i] - reserve[j]) == 1) {
            reserve[j] = -1;
            answer++;
            break;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Math.abs(lost[i] - reserve[j]) == 1이라는 조건은 두 학생 번호가 바로 앞뒤 관계인지 확인하는 조건이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 4번 학생은 3번 또는 5번 학생에게만 빌릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복을 빌려준 학생은 다시 빌려줄 수 없으므로 reserve[j] = -1로 표시했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        Arrays.sort(reserve);
        Arrays.sort(lost);

        int answer = n - lost.length;

        for (int i = 0; i &amp;lt; lost.length; i++) { 
            for (int j = 0; j &amp;lt; reserve.length; j++) {
                if (lost[i] == reserve[j]) {
                    lost[i] = -1;
                    reserve[j] = -1;
                    answer++;                    
                    break;
                } 
            } 
        }

        for (int i = 0; i &amp;lt; lost.length; i++) {
            if (lost[i] == -1) continue;

            for (int j = 0; j &amp;lt; reserve.length; j++) {
                if (reserve[j] == -1) continue;

                if (Math.abs(lost[i] - reserve[j]) == 1) {
                    reserve[j] = -1;
                    answer++;
                    break;
                }
            }
        }
                
        return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 흐름 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 풀이 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. lost와 reserve를 오름차순 정렬한다.
2. 전체 학생 수에서 도난당한 학생 수를 뺀 값을 answer로 시작한다.
3. lost와 reserve에 동시에 있는 학생을 먼저 처리한다.
   - 해당 학생은 수업을 들을 수 있으므로 answer++
   - lost와 reserve에서는 -1로 표시해 제외한다.
4. 남은 lost 학생들을 확인한다.
5. reserve 학생 중 번호 차이가 1인 학생을 찾는다.
6. 빌릴 수 있으면 answer++ 하고, 빌려준 reserve 학생은 -1로 표시한다.
7. 최종 answer를 반환한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 풀이에서는 이중 반복문을 두 번 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 이중 반복문은 lost와 reserve에 동시에 있는 학생을 찾기 위한 반복문이다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; lost.length; i++) {
    for (int j = 0; j &amp;lt; reserve.length; j++) {
        ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 이중 반복문은 체육복을 빌릴 수 있는 학생을 찾기 위한 반복문이다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; lost.length; i++) {
    for (int j = 0; j &amp;lt; reserve.length; j++) {
        ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(L * R)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 L은 lost.length, R은 reserve.length이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최악의 경우 두 배열의 길이가 모두 N에 가깝다면 다음과 같이 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;시간복잡도: O(N^2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬까지 포함하면 더 정확히는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;O(L log L + R log R + L * R)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이중 반복문이 핵심이므로 최악의 경우 O(N^2)으로 정리했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 풀이에서는 별도의 리스트나 상태 배열을 사용하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 lost, reserve 배열 값을 직접 -1로 바꿔서 처리 여부를 표시했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 추가 공간은 거의 사용하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;공간복잡도: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 정렬 과정에서 내부적으로 추가 공간이 사용될 수 있지만, 직접 만든 추가 자료구조 기준으로는 O(1)이라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상태 배열 방식과 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 상태 배열 방식으로도 풀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 배열 방식은 학생마다 체육복을 몇 벌 가지고 있는지 저장하는 방식이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] clothes = new int[n + 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 모든 학생이 체육복을 1벌 가지고 있다고 보고, 도난당한 학생은 1 감소, 여벌이 있는 학생은 1 증가시킨다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;clothes[i] == 0 &amp;rarr; 체육복 없음
clothes[i] == 1 &amp;rarr; 체육복 있음
clothes[i] == 2 &amp;rarr; 여벌 있음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 lost와 reserve에 동시에 있는 학생도 자연스럽게 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 2번 학생이 도난도 당하고 여벌도 있으면 다음과 같이 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;처음: 1
도난 처리: 0
여벌 처리: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 자기 체육복은 입을 수 있지만, 다른 사람에게 빌려줄 여벌은 없는 상태가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 배열 방식은 시간복잡도는 O(N), 공간복잡도는 O(N)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 내가 푼 방식은 기존 배열을 직접 수정해서 공간은 적게 쓰지만, 이중 반복문을 사용하기 때문에 시간복잡도는 O(N^2)이다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;내 풀이: 시간복잡도 O(N^2), 공간복잡도 O(1)
상태 배열 풀이: 시간복잡도 O(N), 공간복잡도 O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 풀이에서 배운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 그리디 문제였지만, 구현 과정에서 배열 인덱스와 배열 원소의 차이를 다시 정리할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 개념이 중요했다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;i, j는 배열 인덱스
lost[i], reserve[j]는 학생 번호
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 번호가 숫자로 되어 있다 보니 인덱스처럼 착각하기 쉬웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 lost와 reserve에 동시에 있는 학생을 먼저 처리해야 한다는 점도 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 처리를 하지 않으면 도난당했지만 여벌도 있는 학생이 다른 학생에게 체육복을 빌려주는 것으로 잘못 계산될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셀프 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 처음에 바로 풀지 못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 체육복 없는 학생 목록과 여벌 체육복이 있는 학생 목록을 따로 관리하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향은 맞았지만, 리스트에 같은 학생이 중복으로 들어가거나 리스트를 순회하면서 삭제하는 문제가 생겼다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간이 부족했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간보다는 구현 과정에서 인덱스와 학생 번호를 헷갈린 것이 더 큰 원인이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 해석을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 큰 방향은 이해했지만, lost와 reserve에 동시에 있는 학생을 먼저 처리해야 한다는 예외 조건을 놓칠 수 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 리스트를 사용하려고 했지만, 오히려 구현이 복잡해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 기존 배열을 -1로 표시하면서 처리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 배열을 사용하면 더 단순하게 풀 수도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엣지 케이스를 놓쳤는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도난당했지만 여벌도 있는 학생이 가장 중요한 엣지 케이스였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 경우이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lost = [2, 4]
reserve = [2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 학생은 자기 체육복 문제만 해결할 수 있고, 다른 학생에게 빌려줄 수 없다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 실수가 있었는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 번호와 배열 인덱스를 헷갈렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 lost[less] = -1처럼 작성하려고 했는데, less는 학생 번호이므로 배열 인덱스로 사용하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바른 방식은 현재 반복문의 인덱스를 사용해 아래처럼 수정하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;lost[i] = -1;
reserve[j] = -1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간 복잡도 판단을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 풀이 기준으로는 이중 반복문을 사용하므로 최악의 경우 O(N^2)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 배열을 사용하면 O(N)으로 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서는 입력 범위가 크지 않기 때문에 내가 작성한 O(N^2) 풀이도 충분히 통과 가능하다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/352</guid>
      <comments>https://mymelody.tistory.com/352#entry352comment</comments>
      <pubDate>Tue, 30 Jun 2026 16:59:37 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 등굣길 | 두 번째 풀이</title>
      <link>https://mymelody.tistory.com/351</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;등굣길 문제를 두 번째로 풀면서 DP 접근 자체는 맞게 잡았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp[y][x]에 시작점부터 (x, y)까지 이동하는 경우의 수를 저장하고, 위쪽과 왼쪽 값을 더하는 방식으로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음 제출한 코드에서는 구현 과정에서 몇 가지를 놓쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mymelody.tistory.com/350&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; 등굣길 - 첫풀이(문제포함)&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 웅덩이 좌표 순서를 반대로 사용했다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 puddles는 [x, y] 형태로 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DP 배열은 행, 열 순서로 사용했기 때문에 dp[y][x] 형태가 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래처럼 작성했다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[puddle[0]][puddle[1]] = -1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 올바른 코드는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[puddle[1]][puddle[0]] = -1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열을 new int[n + 1][m + 1]로 만들었기 때문에,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 인덱스는 세로 길이인 y&lt;/li&gt;
&lt;li&gt;두 번째 인덱스는 가로 길이인 x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 사용해야 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 시작점 값을 초기화하지 않았다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 dp[1][1]을 따로 설정하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 출발점인 (1, 1)까지 도착하는 방법은 이미 한 가지이므로, 시작 전에 반드시 값을 넣어줘야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[1][1] = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 초기값이 없으면 배열은 모두 0에서 시작하고, 이후에도 위쪽과 왼쪽의 0만 더하게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 웅덩이 칸을 다시 계산하고 있었다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 코드에서는 웅덩이를 -1로 표시했지만, 반복문에서 해당 칸을 건너뛰지 않았다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[j][k] = a + b;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드가 실행되면 웅덩이로 표시한 -1 값도 일반 길처럼 다시 계산되어 덮어써질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 현재 위치가 웅덩이인지 먼저 확인하고, 웅덩이라면 계산하지 않도록 수정했다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;if (dp[j][k] == -1) {
    continue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. a, b를 행마다 한 번만 초기화했다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 코드에서는 a, b를 안쪽 반복문 밖에서 선언했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for (int j = 1; j &amp;lt; n + 1; j++) {
    int a = 0;
    int b = 0;

    for (int k = 1; k &amp;lt; m + 1; k++) {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 같은 행에서 다음 열을 계산할 때, 이전 열에서 사용한 a, b 값이 남을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 위쪽이나 왼쪽 칸이 웅덩이인 경우에는 값을 새로 대입하지 못하고, 이전 칸의 값이 그대로 사용될 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 현재 칸을 계산할 때마다 0으로 초기화하도록 수정했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for (int j = 1; j &amp;lt;= n; j++) {
    for (int k = 1; k &amp;lt;= m; k++) {
        int a = 0;
        int b = 0;

        // 현재 칸 계산
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;수정 후 핵심 코드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Solution {
    private static final int MOD = 1_000_000_007;

    public int solution(int m, int n, int[][] puddles) {
        int[][] dp = new int[n + 1][m + 1];

        for (int[] puddle : puddles) {
            dp[puddle[1]][puddle[0]] = -1;
        }

        dp[1][1] = 1;

        for (int y = 1; y &amp;lt;= n; y++) {
            for (int x = 1; x &amp;lt;= m; x++) {

                if (y == 1 &amp;amp;&amp;amp; x == 1) {
                    continue;
                }

                if (dp[y][x] == -1) {
                    continue;
                }

                int fromTop = 0;
                int fromLeft = 0;

                if (dp[y - 1][x] != -1) {
                    fromTop = dp[y - 1][x];
                }

                if (dp[y][x - 1] != -1) {
                    fromLeft = dp[y][x - 1];
                }

                dp[y][x] = (fromTop + fromLeft) % MOD;
            }
        }

        return dp[n][m];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 두 번째 풀이에서는 DP 점화식보다 구현 세부 사항에서 실수했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 DP 문제에서는 아래 내용을 주의해서 확인해야겠다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 시작점 초기값은 설정했는가?
2. 입력 좌표와 배열 인덱스 순서가 같은가?
3. 웅덩이처럼 계산하면 안 되는 칸을 건너뛰고 있는가?
4. 반복문 안에서 사용하는 임시 변수 값이 이전 칸에 영향을 받지 않는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/351</guid>
      <comments>https://mymelody.tistory.com/351#entry351comment</comments>
      <pubDate>Tue, 30 Jun 2026 16:44:34 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 등굣길</title>
      <link>https://mymelody.tistory.com/350</link>
      <description>&lt;h1&gt;프로그래머스 등굣길, DP로 풀기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 프로그래머스의 &lt;b&gt;등굣길&lt;/b&gt; 문제를 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42898&quot;&gt; 프로그래머스 등굣길 문제&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에서 학교까지 가는 길이 격자 형태로 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집은 왼쪽 위에 있고, 학교는 오른쪽 아래에 있다.&lt;br /&gt;이동은 오른쪽과 아래쪽으로만 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 물웅덩이가 있는 칸은 지나갈 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 집에서 학교까지 갈 수 있는 최단 경로의 개수를 구해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 경로의 수가 매우 커질 수 있으므로 정답은 1,000,000,007로 나눈 나머지를 반환해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;DP, 동적 계획법&lt;/b&gt; 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 DFS로 모든 경로를 탐색하면 되지 않을까 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 칸에서 이동할 수 있는 방향은 최대 두 가지이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;오른쪽으로 이동
아래쪽으로 이동
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 DFS로 모든 경로를 탐색할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 격자의 크기가 커지면 가능한 경로 수가 너무 많아져 시간초과가 날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에, 각 칸까지 오는 경로의 수를 저장해두고 &lt;b&gt;재사용&lt;/b&gt;하는 &lt;b&gt;DP 방식&lt;/b&gt;으로 풀어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DFS로 풀면 왜 안 될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 가능한 모든 경로를 직접 탐색하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에서 학교까지 가려면 총 이동 횟수는 대략 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;오른쪽 이동: m - 1번
아래쪽 이동: n - 1번
총 이동 횟수: m + n - 2번
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 이동마다 오른쪽 또는 아래쪽 두 가지 선택지가 생길 수 있으므로, 단순 DFS로 탐색하면 대략 다음과 같은 시간복잡도가 된다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;DFS 시간복잡도: O(2^(m+n))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히는 오른쪽 이동과 아래쪽 이동을 배치하는 조합의 수만큼 경로가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 100 x 100 격자라면 오른쪽 99번, 아래쪽 99번을 이동해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;총 이동 횟수 = 198번
가능한 경로 수 = C(198, 99)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값은 엄청나게 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, DFS로 모든 경로를 하나씩 탐색하면 시간초과가 날 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS의 공간복잡도는 재귀 호출 깊이만큼이므로 대략 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;DFS 공간복잡도: O(m + n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간복잡도만 보면 괜찮아 보일 수 있지만, 문제는 시간복잡도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 경로 수가 너무 많기 때문에 DFS 완전탐색은 적절하지 않다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 범위와 시간 복잡도 판단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등굣길 문제에서 격자의 크기는 최대 100 x 100이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP를 사용하면 모든 칸을 한 번씩만 계산하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격자의 세로 길이를 n, 가로 길이를 m이라고 하면 DP의 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;DP 시간복잡도: O(n * m)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 크기에서도,&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;100 * 100 = 10,000
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;칸만 계산하면 되므로 충분히 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간복잡도는 dp 배열과 물웅덩이를 표시하는 배열을 사용하므로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;DP 공간복잡도: O(n * m)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DP 배열의 의미 정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP 문제에서 가장 먼저 해야 할 일은 dp 배열의 의미를 정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 다음과 같이 정의했다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y][x] = 집에서 (y, x) 칸까지 오는 방법의 수
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 y는 세로 방향, x는 가로 방향이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;점화식 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등굣길 문제에서는 오른쪽과 아래쪽으로만 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떤 칸 (y, x)에 도착하기 직전에는 어디에 있었을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 경우는 두 가지이다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;위쪽 칸에서 내려오는 경우       &amp;rarr; dp[y - 1][x]
왼쪽 칸에서 오른쪽으로 오는 경우 &amp;rarr; dp[y][x - 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현재 칸까지 오는 방법의 수는 위쪽 칸까지 오는 방법의 수와 왼쪽 칸까지 오는 방법의 수를 더하면 된다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y][x] = dp[y - 1][x] + dp[y][x - 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 현재 칸이 물웅덩이라면 지나갈 수 없으므로 해당 칸은 계산하지 않는다.&lt;br /&gt;물웅덩이 칸의 경로 수는 0으로 보면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 dp[1][1] = 1로 시작할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 부분이 조금 헷갈렸다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[1][1] = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 이동하지 않았는데 왜 1이라고 하는지 의문이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DP에서는 &lt;b&gt;출발점에 서 있는 상태 자체를 경로 1개&lt;/b&gt;로 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;집까지 오는 방법 = 처음부터 집에 서 있다 = 1가지
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 dp[1][1]을 0으로 두면 오른쪽 칸과 아래쪽 칸도 모두 0이 되고, 결국 모든 칸의 값이 0이 되어버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 출발점은 반드시 1로 시작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dp_등굣길.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u3Dp1/dJMcajihEFh/TJ4EJDe80cO9oJpDqh4kj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u3Dp1/dJMcajihEFh/TJ4EJDe80cO9oJpDqh4kj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u3Dp1/dJMcajihEFh/TJ4EJDe80cO9oJpDqh4kj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu3Dp1%2FdJMcajihEFh%2FTJ4EJDe80cO9oJpDqh4kj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;531&quot; data-filename=&quot;dp_등굣길.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;좌표에서 헷갈렸던 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서 가장 헷갈렸던 부분은 puddles 좌표였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스에서 물웅덩이는 다음과 같이 주어진다.&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;puddles = [[2, 3]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 뜻은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;x = 2
y = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, puddles는 [x, y] 순서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Java 2차원 배열은 보통 다음과 같이 접근한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;array[행][열]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행은 세로 방향이고, 열은 가로 방향이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Java 배열에서는 다음과 같이 접근해야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;array[y][x]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 물웅덩이를 표시할 때는 아래처럼 해야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;isBlocked[y][x] = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 부분을 반대로 작성했다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;isBlocked[x][y] = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 물웅덩이 좌표가 [2, 2]처럼 x와 y가 같은 경우라서 우연히 맞을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 숨은 테스트에서는 [3, 2], [1, 4]처럼 x와 y가 다른 좌표가 나올 수 있기 때문에 틀리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 꼭 기억해야 할 부분은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;puddles는 [x, y]
Java 배열은 [y][x]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 이렇게 외우면 된다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;좌표는 가로 먼저
배열은 세로 먼저
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;나머지 연산을 중간에 해야 하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서는 정답을 1,000,000,007로 나눈 나머지를 반환하라고 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 마지막에 한 번만 나누면 되지 않을까 생각했다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;return dp[n][m] % 1000000007;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 하면 중간에 계산되는 경로 수가 너무 커져서 int 범위를 넘을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 int는 대략 21억 정도까지만 저장할 수 있다.&lt;br /&gt;경로의 수는 빠르게 커질 수 있으므로, 마지막에 나누기 전에 이미 값이 깨질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DP 값을 갱신할 때마다 나머지 연산을 해줘야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y][x] = (dp[y - 1][x] + dp[y][x - 1]) % 1000000007;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 연산에는 다음 성질이 있다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;(a + b) % M = ((a % M) + (b % M)) % M
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 중간에 계속 % MOD를 해도 최종 나머지 값은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;코딩테스트에서 &amp;ldquo;정답이 커질 수 있으니 특정 수로 나눈 나머지를 반환하라&amp;rdquo;는 문장이 나오면, &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;계산 중간마다 MOD 처리를 해야 한다고 생각하면 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 최종적으로 작성한 코드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Solution {
    public int solution(int m, int n, int[][] puddles){
        
        // 문제유형 : DP 
        int answer = 0;
        
        boolean[][] isBlocked = new boolean[n+1][m+1];
        int[][] dp = new int[n+1][m+1];
        
        for(int[] block : puddles) {
            int x = block[0];
            int y = block[1];
            isBlocked[y][x]=true;
        }

        dp[1][1] = 1;

        for (int x1=1; x1&amp;lt;m+1; x1++) {
            for (int y1=1; y1&amp;lt;n+1; y1++) {
                if (!isBlocked[y1][x1] &amp;amp;&amp;amp; dp[y1][x1] == 0) {
                    dp[y1][x1] = (dp[y1-1][x1] + dp[y1][x1-1]) % 1000000007;
                 }
            }
        }

        return dp[n][m];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 피드백&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드도 통과 가능한 풀이이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 아래 부분은 꼭 필요하지 않기 때문에 제거 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y1][x1] == 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 칸은 반복문에서 한 번씩만 계산되기 때문에, 물웅덩이 여부와 시작점 여부만 명확하게 처리하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가독성 측면에서는 x1, y1보다 x, y 또는 row, col처럼 의미가 드러나는 변수명을 쓰는 것이 더 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다듬은 코드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Solution {
    public int solution(int m, int n, int[][] puddles) {
        
        int MOD = 1000000007;
        
        boolean[][] isBlocked = new boolean[n + 1][m + 1];
        int[][] dp = new int[n + 1][m + 1];
        
        for (int[] block : puddles) {
            int x = block[0];
            int y = block[1];
            isBlocked[y][x] = true;
        }
        
        dp[1][1] = 1;
        
        for (int y = 1; y &amp;lt;= n; y++) {
            for (int x = 1; x &amp;lt;= m; x++) {
                
                if (x == 1 &amp;amp;&amp;amp; y == 1) {
                    continue;
                }
                
                if (isBlocked[y][x]) {
                    continue;
                }
                
                dp[y][x] = (dp[y - 1][x] + dp[y][x - 1]) % MOD;
            }
        }
        
        return dp[n][m];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP 배열의 모든 칸을 한 번씩 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격자의 세로 길이가 n, 가로 길이가 m이므로 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;시간복잡도: O(n * m)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물웅덩이를 표시하는 데는 puddles.length만큼 시간이 걸리지만, 전체적으로는 격자를 순회하는 O(n * m)이 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp 배열과 isBlocked 배열을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 배열 모두 크기가 (n + 1) x (m + 1)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;공간복잡도: O(n * m)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;복잡도 정리&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;DFS 시간복잡도: O(2^(m+n))
DFS 공간복잡도: O(m+n)

DP 시간복잡도: O(n * m)
DP 공간복잡도: O(n * m)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 경로를 모두 직접 탐색하는 DFS보다, 각 칸까지 오는 경로 수를 저장해두는 DP가 적절하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 풀이에서 배운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 DP 점화식을 세우는 연습이 되는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 것은 dp[y][x]가 무엇을 의미하는지 먼저 정하는 것이었다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y][x] = 집에서 (y, x)까지 오는 방법의 수
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 의미를 정하니 현재 칸은 위쪽 칸과 왼쪽 칸에서 올 수 있다는 점을 떠올릴 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 점화식은 아래처럼 만들 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[y][x] = dp[y - 1][x] + dp[y][x - 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 이번 문제를 통해 좌표와 배열 인덱스의 차이도 다시 정리할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;puddles는 [x, y]
Java 배열은 [y][x]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 정말 헷갈렸고, 예제만 보고는 실수를 찾기 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 정답을 특정 수로 나눈 나머지를 반환해야 하는 경우에는 계산 마지막에만 나누는 것이 아니라, 중간 계산마다 % MOD를 해줘야 한다는 것도 배웠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셀프 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 처음에 바로 풀지 못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS로 모든 경로를 탐색하면 될 것 같았지만, 입력 크기를 생각하면 시간초과가 날 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DP로 전환해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 DP 점화식을 세우는 과정이 아직 익숙하지 않았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간이 부족했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 푸는 데 시간 자체가 부족했다기보다, 좌표 처리와 점화식을 이해하는 데 시간이 걸렸다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 해석을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동 방향이 오른쪽과 아래쪽뿐이라는 점은 이해했지만, puddles 좌표가 [x, y]로 주어지고 Java 배열은 [y][x]로 접근해야 한다는 점을 처음에 잘못 처리했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 DFS를 생각했지만, 최종적으로는 DP 배열과 물웅덩이 체크용 boolean 배열을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 DP 선택이 적절했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엣지 케이스를 놓쳤는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채점 전 코드 테스트시 주어진 입력 값처럼 물웅덩이 좌표가 [2, 2]처럼 x와 y가 같으면 실수가 드러나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 [3, 2]처럼 x와 y가 다른 경우에는 isBlocked[x][y]로 저장하면 틀린 위치가 막힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 시작점 dp[1][1] = 1을 따로 설정해야 한다는 점도 중요했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 실수가 있었는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 isBlocked[x][y]로 작성해서 물웅덩이 위치가 잘못 표시되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 나머지 연산을 마지막에만 하려고 했는데, 중간 계산에서 오버플로우가 날 수 있으므로 매번 % MOD를 적용해야 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간 복잡도 판단을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 DFS도 가능할지 고민했지만, 모든 경로를 탐색하면 경우의 수가 너무 많아질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP로 풀면 모든 칸을 한 번씩만 계산하므로 O(n * m)에 해결할 수 있다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/350</guid>
      <comments>https://mymelody.tistory.com/350#entry350comment</comments>
      <pubDate>Mon, 15 Jun 2026 15:24:50 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 타겟 넘버 | 두번째풀이</title>
      <link>https://mymelody.tistory.com/349</link>
      <description>&lt;h1&gt;프로그래머스 타겟 넘버, DFS로 모든 경우 탐색하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2937; text-align: start;&quot;&gt;3주 전에 풀었던&lt;/span&gt;&amp;nbsp;프로그래머스의 &lt;b&gt;타겟 넘버&lt;/b&gt; 문제를 다시 풀어보았다.&lt;br /&gt;&lt;br /&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43165&quot;&gt; &lt;/a&gt;&lt;a href=&quot;https://mymelody.tistory.com/339&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; 처음 풀이&amp;nbsp; programmers)&amp;nbsp;타겟&amp;nbsp;넘버,&amp;nbsp;재귀&amp;nbsp;연습하기 &lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43165&quot;&gt;  프로그래머스 타겟 넘버 문제&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n개의 음이 아닌 정수들이 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 숫자들의 순서는 바꾸지 않고, 각 숫자 앞에 + 또는 -를 붙여서 원하는 타겟 넘버를 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 숫자들이 있을 때,&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[1, 1, 1, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타겟 넘버 3을 만드는 방법은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;-1 +1 +1 +1 +1 = 3
+1 -1 +1 +1 +1 = 3
+1 +1 -1 +1 +1 = 3
+1 +1 +1 -1 +1 = 3
+1 +1 +1 +1 -1 = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 5가지 방법이 있으므로 정답은 5이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제한 사항&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers의 개수는 2개 이상 20개 이하
각 숫자는 1 이상 50 이하
target은 1 이상 1000 이하
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예시는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 60.3488%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1163%;&quot;&gt;numberstargetreturn&lt;/td&gt;
&lt;td style=&quot;width: 19.3024%;&quot;&gt;target&lt;/td&gt;
&lt;td style=&quot;width: 15.8139%;&quot;&gt;return&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1163%;&quot;&gt;[1, 1, 1, 1, 1]&lt;/td&gt;
&lt;td style=&quot;width: 19.3024%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 15.8139%;&quot;&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1163%;&quot;&gt;[4, 1, 2, 1]&lt;/td&gt;
&lt;td style=&quot;width: 19.3024%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 15.8139%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;

&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;DFS / 완전탐색&lt;/b&gt; 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 숫자마다 선택할 수 있는 경우는 두 가지이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 숫자를 더하는 경우
현재 숫자를 빼는 경우
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 모든 숫자에 대해 +, - 두 가지 선택을 끝까지 해보면서 target이 되는 경우의 수를 세면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 범위 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 길이는 최대 20이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 숫자마다 +, - 두 가지 경우가 있으므로 전체 경우의 수는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^N
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 N이 20이면,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^20 = 1,048,576
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 100만 가지 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100만 가지 정도는 DFS로 모두 탐색해도 충분히 가능하다고 판단했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 허용선 판단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 각 숫자마다 두 가지 선택지가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;시간복잡도: O(2^N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 +, - 두 가지니까 O(2N)처럼 생각할 수도 있지만, 실제로는 숫자가 하나 늘어날 때마다 경우의 수가 2배씩 늘어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 2 * N이 아니라 2^N으로 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용할 자료구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 자료구조를 사용하기보다는 &lt;b&gt;재귀 DFS&lt;/b&gt;를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 함수에는 현재 인덱스와 현재까지의 합을 넘겨주면 된다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;index: 현재 몇 번째 숫자를 보고 있는지
sum: 현재까지 계산된 합
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS가 끝까지 내려가면, 즉 모든 숫자를 다 사용하면 sum이 target과 같은지 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 작성한 풀이 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 0번째 숫자를 밖에서 먼저 처리하는 방식으로 풀었다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(0, numbers[0], numbers, target);
dfs(0, -numbers[0], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 0번째 숫자를 더하는 경우와 빼는 경우를 먼저 나눈 뒤 DFS를 시작하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 DFS 내부에서는 다음 인덱스 값을 더하거나 빼면서 탐색했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(i + 1, sum + numbers[i + 1], numbers, target);
dfs(i + 1, sum - numbers[i + 1], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드도 정답 풀이가 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 작성한 코드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Solution {
    
    // 자료구조 : DFS
    // 풀이시간 : 25분/30분
    // 공간복잡도 : O(N) // 스택이 쌓인다.
    // 시간복잡도 : O(2^N) // 두 가지 경우 +, -
    int count = 0;
    
    public int solution(int[] numbers, int target) {
        count = 0;
        dfs(0, numbers[0], numbers, target);
        dfs(0, -numbers[0], numbers, target);
        return count;
    }
    
    public void dfs(int i, int sum, int[] numbers, int target) {
        if (i == numbers.length - 1) {
            if (sum == target) {
                count++;
            }
            return;
        }

        dfs(i + 1, sum + numbers[i + 1], numbers, target);
        dfs(i + 1, sum - numbers[i + 1], numbers, target);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 경우의 수를 저장할 count 변수를 선언했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int count = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solution()이 실행될 때마다 혹시 모를 누적을 방지하기 위해 count를 0으로 초기화했다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;count = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 0번째 숫자를 더하는 경우와 빼는 경우로 DFS를 시작했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(0, numbers[0], numbers, target);
dfs(0, -numbers[0], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 내부에서는 현재 인덱스가 마지막 숫자인지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;if (i == numbers.length - 1) {
    if (sum == target) {
        count++;
    }
    return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 숫자까지 계산한 상태에서 sum이 target과 같다면 방법의 수를 하나 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 마지막 숫자가 아니라면 다음 숫자를 더하는 경우와 빼는 경우를 각각 탐색한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(i + 1, sum + numbers[i + 1], numbers, target);
dfs(i + 1, sum - numbers[i + 1], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;헷갈렸던 부분: 0번째 값은 언제 더해지는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS를 더 일반적인 형태로 작성하면 보통 이렇게 시작한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(0, 0, numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 방식이 조금 헷갈렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sum이 0으로 시작하면 0번째 값은 언제 더해지는지 의문이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DFS 함수 안에서 아래처럼 처리하면,&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;dfs(index + 1, sum + numbers[index], numbers, target);
dfs(index + 1, sum - numbers[index], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 index가 0이므로 실제로는 이렇게 실행된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(1, 0 + numbers[0], numbers, target);
dfs(1, 0 - numbers[0], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 0번째 값도 DFS 내부에서 자연스럽게 더하거나 빼게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 작성한 코드는 0번째 값을 solution()에서 먼저 처리했고, 추천되는 방식은 0번째 값도 DFS 내부에서 처리한다는 차이가 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 깔끔한 DFS 형태&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 풀이도 정답이지만, DFS 기본 형태로는 아래 코드가 더 깔끔하다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;class Solution {
    
    int count = 0;
    
    public int solution(int[] numbers, int target) {
        count = 0;
        dfs(0, 0, numbers, target);
        return count;
    }
    
    public void dfs(int index, int sum, int[] numbers, int target) {
        if (index == numbers.length) {
            if (sum == target) {
                count++;
            }
            return;
        }
        
        dfs(index + 1, sum + numbers[index], numbers, target);
        dfs(index + 1, sum - numbers[index], numbers, target);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 다음 의미가 코드에 바로 드러난다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;index번째 숫자를 더한다.
index번째 숫자를 뺀다.
다음 index로 넘어간다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 숫자만 따로 처리하지 않아도 되기 때문에 DFS 문제에서는 이 형태를 기본 템플릿처럼 익혀두면 좋을 것 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 숫자마다 선택지는 두 가지이다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;+ 선택
- 선택
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 숫자가 N개라면 전체 경우의 수는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^N
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;시간복잡도: O(2^N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 길이는 최대 20이므로 최대 경우의 수는 약 100만 가지이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^20 = 1,048,576
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도는 완전탐색으로 해결 가능하다고 판단했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 배열이나 리스트를 만들지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 재귀 DFS를 사용했기 때문에 호출 스택이 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 깊이는 최대 numbers의 길이만큼이므로 공간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;공간복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;복잡도 정리&lt;/h2&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;시간복잡도: O(2^N)
공간복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 케이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 numbers의 길이가 최소 2 이상이기 때문에 빈 배열은 고려하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 DFS를 작성할 때 인덱스 범위를 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 작성한 방식에서는 아래처럼 numbers[i + 1]을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dfs(i + 1, sum + numbers[i + 1], numbers, target);
dfs(i + 1, sum - numbers[i + 1], numbers, target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 반드시 그 전에 마지막 인덱스인지 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;if (i == numbers.length - 1) {
    ...
    return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건이 없으면 i + 1이 배열 범위를 넘어갈 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 풀이에서 배운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 DFS의 기본 구조를 다시 연습할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 내용을 정리할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;각 숫자마다 +, - 두 가지 경우가 생긴다.
전체 경우의 수는 O(2^N)이다.
재귀 DFS를 사용하면 공간복잡도는 O(N)이다.
0번째 숫자를 밖에서 먼저 처리할 수도 있고, DFS 내부에서 처리할 수도 있다.
DFS는 보통 dfs(index, sum) 형태로 작성하면 더 깔끔하다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 O(2N)처럼 시간복잡도를 생각했지만, 경우의 수가 두 배씩 늘어나므로 O(2^N)으로 보는 것이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 0번째 숫자를 언제 더하는지 헷갈렸는데, dfs(0, 0)으로 시작해도 DFS 내부에서 numbers[0]을 더하거나 빼면서 자연스럽게 처리된다는 점을 이해했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셀프 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 처음에 바로 풀지 못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS로 풀어야 한다는 방향은 잡았지만, 재귀 함수의 인자를 어떻게 구성해야 할지 조금 헷갈렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 0번째 숫자를 밖에서 먼저 처리할지, DFS 내부에서 처리할지 고민했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간이 부족했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 풀이 시간은 25분 정도 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 시간 안에는 풀었지만, DFS 구조를 정리하는 데 시간이 조금 걸렸다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 해석을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 해석 자체는 크게 틀리지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 숫자마다 더하거나 빼는 모든 경우를 탐색해야 한다는 점은 잘 파악했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료구조를 따로 사용하는 문제는 아니었고, DFS 재귀를 사용한 선택은 적절했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엣지 케이스를 놓쳤는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 범위를 주의해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 numbers[i + 1]을 사용할 때 마지막 인덱스를 먼저 확인하지 않으면 배열 범위를 벗어날 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 실수가 있었는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 value라는 파라미터를 넘겨놓고 실제로 sum에 반영하지 않는 실수가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 value 파라미터를 제거하고 sum만 넘기는 방식으로 수정하면서 코드가 더 단순해졌다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간 복잡도 판단을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 O(2N)이라고 적었지만, 실제로는 O(2^N)이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 숫자마다 경우의 수가 2배씩 늘어나기 때문이다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/349</guid>
      <comments>https://mymelody.tistory.com/349#entry349comment</comments>
      <pubDate>Sun, 14 Jun 2026 23:31:58 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 더 맵게</title>
      <link>https://mymelody.tistory.com/348</link>
      <description>&lt;h1&gt;프로그래머스&amp;nbsp; | 더 맵게&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 프로그래머스의 &lt;b&gt;더 맵게&lt;/b&gt; 문제를 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 링크는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42626&quot;&gt; 프로그래머스 더 맵게 문제&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶어 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 스코빌 지수가 가장 낮은 두 개의 음식을 골라 아래 방식으로 섞는다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;섞은 음식의 스코빌 지수
= 가장 맵지 않은 음식의 스코빌 지수
+ (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 모든 음식의 스코빌 지수를 K 이상으로 만들 수 없다면 -1을 반환해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입출력 예시&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 67.093%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.2016%; text-align: center;&quot;&gt;&lt;b&gt;scovilleKreturn&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.0897%; text-align: center;&quot;&gt;&lt;b&gt;K&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.6855%; text-align: center;&quot;&gt;&lt;b&gt;return&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.2016%; text-align: center;&quot;&gt;[1, 2, 3, 9, 10, 12]&lt;/td&gt;
&lt;td style=&quot;width: 19.0897%; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 24.6855%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 직접 따라가 보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 음식의 스코빌 지수는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[1, 2, 3, 9, 10, 12]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 맵지 않은 음식 1과 두 번째로 맵지 않은 음식 2를 섞는다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 + (2 * 2) = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 음식 목록은 다음과 같이 된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[5, 3, 9, 10, 12]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 가장 작은 값이 3이므로 K = 7보다 작다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 가장 작은 두 값인 3과 5를 섞는다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;3 + (5 * 2) = 13
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 음식 목록은 다음과 같이 된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[13, 9, 10, 12]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 음식의 스코빌 지수가 7 이상이 되었으므로 섞은 횟수는 2가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;힙, 우선순위 큐 문제&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 배열을 정렬한 뒤 작은 값부터 차례대로 섞으면 될 것 같다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 단순 정렬만으로는 해결하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 음식을 섞어서 만든 새로운 스코빌 지수도 다시 음식 목록에 들어가기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 매번 아래 작업을 반복해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 가장 작은 값 2개를 꺼낸다.
2. 새 스코빌 지수를 만든다.
3. 새 값을 다시 넣는다.
4. 다시 가장 작은 값을 확인한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매번 가장 작은 값을 빠르게 꺼낼 수 있는 PriorityQueue를 사용해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 범위 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 사항은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;scoville의 길이는 2 이상 1,000,000 이하
K는 0 이상 1,000,000,000 이하
scoville의 원소는 각각 0 이상 1,000,000 이하
모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우 -1 반환
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scoville의 길이가 최대 1,000,000이므로, 매번 배열을 정렬하거나 모든 값을 반복해서 탐색하는 방식은 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 한 번 정렬하는 것도 부족하다.&lt;br /&gt;새로 만든 값이 다시 들어가기 때문에, 매번 가장 작은 두 값을 빠르게 찾을 수 있어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 허용선 판단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 크기가 최대 1,000,000이므로 O(N^2) 방식은 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 큐를 사용하면 값을 넣거나 꺼낼 때 O(log N)이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 음식을 큐에 넣고, 섞는 과정에서도 poll()과 offer()를 사용하므로 전체 시간 복잡도는 O(N log N)으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도면 입력 범위 내에서 적절한 풀이 방식이라고 판단했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 접근에서 놓친 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 배열을 정렬해서 작은 값부터 순서대로 더해가면 될 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방식은 문제의 실제 섞는 과정과 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 경우를 생각해볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[1, 2, 3, 4, 100], K = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 정렬된 배열에서 앞에서부터 누적하면,&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 + 2 * 2 = 5
5 + 3 * 2 = 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 문제에서는 섞은 값 5가 다시 음식 목록에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 과정은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1과 2를 섞음 &amp;rarr; 5
남은 음식: [3, 4, 5, 100]

3과 4를 섞음 &amp;rarr; 11
남은 음식: [5, 11, 100]

5와 11을 섞음 &amp;rarr; 27
남은 음식: [27, 100]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 새로 만들어진 값도 다시 비교 대상이 되기 때문에 단순 정렬 후 순차 처리로는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분 때문에 이 문제는 정렬 문제가 아니라 우선순위 큐 문제라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용할 자료구조: PriorityQueue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 PriorityQueue는 기본적으로 작은 값이 먼저 나오는 최소 힙 구조이다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;Long&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 매번 가장 맵지 않은 음식 2개를 꺼내야 하므로 PriorityQueue가 잘 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용한 주요 메서드는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;offer() : 큐에 값 넣기
poll()  : 가장 작은 값 꺼내기
peek()  : 가장 작은 값 확인하기
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이 방향&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. PriorityQueue를 만든다.
2. scoville 배열의 값을 모두 PriorityQueue에 넣는다.
3. 가장 작은 값이 K 이상이면 종료한다.
4. 가장 작은 값이 K보다 작으면 가장 작은 값 2개를 꺼낸다.
5. 문제의 공식대로 새 스코빌 지수를 만든다.
6. 새 값을 다시 PriorityQueue에 넣는다.
7. 섞은 횟수를 증가시킨다.
8. 더 이상 섞을 수 없는데 K 미만이면 -1을 반환한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 조건은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.peek() &amp;lt; K)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음식이 2개 이상 있어야 섞을 수 있고, 가장 작은 값이 K보다 작을 때만 섞으면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int solution(int[] scoville, int K) {
        int cnt = 0;
        PriorityQueue&amp;lt;Long&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();

        for (int i = 0; i &amp;lt; scoville.length; i++) {
            pq.offer(Long.valueOf(scoville[i]));
        }

        while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.peek() &amp;lt; K) {
            Long first = pq.poll();
            Long second = pq.poll();

            Long result = first + (second * 2);
            pq.offer(result);

            cnt++;
        }

        if (pq.peek() &amp;lt; K) {
            return -1;
        }

        return cnt;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 PriorityQueue를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;Long&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PriorityQueue는 기본적으로 작은 값이 먼저 나오기 때문에, 이 문제에서 가장 작은 스코빌 지수를 꺼내기에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 scoville 배열의 값을 모두 큐에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; scoville.length; i++) {
    pq.offer(Long.valueOf(scoville[i]));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 Long을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코빌 지수를 섞는 과정에서 값이 커질 수 있기 때문에 int보다 Long으로 처리하는 것이 더 안전하다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 가장 작은 값이 K보다 작고, 음식이 2개 이상 남아 있으면 계속 섞는다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.peek() &amp;lt; K)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문 안에서는 가장 작은 값 2개를 꺼낸다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Long first = pq.poll();
Long second = pq.poll();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 문제에서 주어진 공식대로 새 스코빌 지수를 만든다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Long result = first + (second * 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만든 값을 다시 우선순위 큐에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;pq.offer(result);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번 섞었으므로 횟수를 증가시킨다.&lt;/p&gt;
&lt;pre class=&quot;brainfuck&quot;&gt;&lt;code&gt;cnt++;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복이 끝난 후에도 가장 작은 값이 K보다 작다면, 더 이상 모든 음식을 K 이상으로 만들 수 없다는 뜻이므로 -1을 반환한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;if (pq.peek() &amp;lt; K) {
    return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 케이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 주의해야 할 예외 케이스는 더 이상 섞을 수 없는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 음식이 하나만 남았는데 그 값이 아직 K보다 작다면 더 이상 두 음식을 섞을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 반복문의 조건에 pq.size() &amp;gt;= 2를 넣었다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.peek() &amp;lt; K)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 반복문이 끝난 뒤에도 가장 작은 값이 K보다 작으면 -1을 반환한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;if (pq.peek() &amp;lt; K) {
    return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scoville의 길이를 N이라고 하면, 먼저 모든 값을 우선순위 큐에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; scoville.length; i++) {
    pq.offer(Long.valueOf(scoville[i]));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;offer()는 한 번에 O(log N)이 걸리므로, 전체 삽입은 O(N log N)으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 섞는 과정에서는 매번 아래 연산을 수행한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;poll()
poll()
offer()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 연산은 O(log N)이고, 최악의 경우 여러 번 반복될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 전체 시간 복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;시간 복잡도: O(N log N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Java의 PriorityQueue에 한 번에 넣는 방식이 아니라 하나씩 offer()를 사용했기 때문에 삽입 과정도 O(N log N)으로 정리했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 스코빌 지수를 PriorityQueue에 넣기 때문에 입력 크기만큼의 추가 공간이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공간 복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;공간 복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;복잡도 정리&lt;/h2&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;시간 복잡도: O(N log N)
공간 복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 풀이에서 배운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 가장 크게 배운 점은 &lt;b&gt;한 번 정렬하는 것과 우선순위 큐를 사용하는 것은 다르다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 정렬을 한 뒤 앞에서부터 차례대로 처리하면 될 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 새로 만든 스코빌 지수가 다시 음식 목록에 들어가고, 그 값도 다시 가장 작은 값 후보가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매번 가장 작은 값 2개를 빠르게 꺼낼 수 있는 PriorityQueue를 사용해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 PriorityQueue에 배열 값을 넣는 방법도 다시 연습할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;Long&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();

for (int i = 0; i &amp;lt; scoville.length; i++) {
    pq.offer(Long.valueOf(scoville[i]));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 통해 정렬 문제처럼 보이더라도, 반복적으로 최소값이나 최대값을 꺼내야 한다면 힙이나 우선순위 큐를 떠올려야 한다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 최대 힙을 꺼내야한다면, 아래와 같이 선언하자.&lt;/p&gt;
&lt;pre id=&quot;code_1781443113952&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;Long&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;(Collections.reverseOrder());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셀프 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 처음에 바로 풀지 못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 한 번 정렬한 뒤 작은 값부터 차례대로 섞으면 된다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 섞어서 만든 값이 다시 음식 목록에 들어가기 때문에, 단순 정렬만으로는 해결할 수 없었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간이 부족했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 풀이 자체보다 자료구조 선택에서 시간이 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬과 우선순위 큐의 차이를 생각하는 데 시간이 필요했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 해석을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &amp;ldquo;가장 작은 값부터 섞는다&amp;rdquo;는 부분만 보고 정렬을 떠올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중요한 조건은 &amp;ldquo;섞은 음식도 다시 음식 목록에 포함된다&amp;rdquo;는 점이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 배열 정렬을 사용하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 매번 가장 작은 값 2개를 꺼내야 하기 때문에 PriorityQueue가 더 적절했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엣지 케이스를 놓쳤는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 섞을 수 없는 경우를 주의해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음식이 1개만 남았는데 아직 K보다 작다면 -1을 반환해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 실수가 있었는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Integer와 Long 타입을 섞어서 사용하면서 오류가 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PriorityQueue&amp;lt;Long&amp;gt;을 사용한다면 poll()로 꺼낸 값도 Long으로 받아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 변수명 오타나 괄호 실수도 있었기 때문에, 최종 제출 전에는 컴파일 오류가 없는지 확인하는 습관이 필요하다고 느꼈다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간 복잡도 판단을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 정렬 한 번으로 해결할 수 있다고 생각했지만, 실제로는 매번 최소값을 다시 찾아야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 O(N log N)으로 동작하는 우선순위 큐 풀이가 필요했다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/348</guid>
      <comments>https://mymelody.tistory.com/348#entry348comment</comments>
      <pubDate>Sun, 14 Jun 2026 22:21:12 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 가장 큰 수</title>
      <link>https://mymelody.tistory.com/347</link>
      <description>&lt;h1&gt;프로그래머스 가장 큰 수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 프로그래머스의 &lt;b&gt;가장 큰 수&lt;/b&gt; 문제를 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot;&gt;  프로그래머스 가장 큰 수 문제&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 주어진 정수가 아래와 같다면,&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[6, 10, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들 수 있는 수는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;6102, 6210, 1062, 1026, 2610, 2106
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 가장 큰 수는 6210이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 배열 numbers가 주어졌을 때 숫자의 순서를 재배치해서 만들 수 있는 가장 큰 수를 문자열로 반환해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제한 사항&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers의 길이는 1 이상 100,000 이하
numbers의 원소는 0 이상 1,000 이하
정답이 너무 클 수 있으므로 문자열로 반환
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예시는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbersreturn&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[6, 10, 2]&lt;/td&gt;
&lt;td&gt;&quot;6210&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[3, 30, 34, 5, 9]&lt;/td&gt;
&lt;td&gt;&quot;9534330&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;정렬 문제&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 일반적인 오름차순, 내림차순 정렬이 아니라 직접 정렬 기준을 만들어야 하는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 숫자를 큰 순서대로 정렬하면 될 것 같았지만, 실제로는 숫자 자체의 크기보다 &lt;b&gt;두 숫자를 이어 붙였을 때 어떤 순서가 더 큰 수를 만드는지&lt;/b&gt;를 비교해야 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 범위 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 길이는 최대 100,000이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모든 순열을 만들어서 가장 큰 수를 찾는 방식은 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 숫자가 10개만 있어도 경우의 수가 매우 커지기 때문에, 모든 조합을 직접 확인하는 방식은 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 결국 숫자들을 어떤 기준으로 정렬한 뒤 이어 붙이는 방식으로 접근해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 허용선 판단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 크기가 최대 100,000이므로 O(N^2) 방식은 부담될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬을 사용하면 일반적으로 O(N log N)에 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 문제는 직접 Comparator 기준을 만들어 정렬하는 방향으로 풀면 시간 복잡도 측면에서 적절하다고 판단했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 생각한 접근&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 숫자를 큰 순서대로 정렬하면 될 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[3, 30, 34, 5, 9]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 있다면 숫자를 내림차순으로 정렬해서 붙이면 되지 않을까 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방식은 틀린 접근이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 3과 30을 비교해보면 숫자 자체로는 30이 더 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 두 숫자를 이어 붙이면 결과가 달라진다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;3 + 30 = 330
30 + 3 = 303
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;330이 303보다 크기 때문에, 이 경우에는 3이 30보다 앞에 와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 문제에서는 숫자 자체의 크기가 아니라,&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a + b
b + a
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 어떤 조합이 더 큰지를 기준으로 정렬해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용할 자료구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 int[] numbers 배열을 바로 정렬하려고 했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Arrays.sort(numbers, ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Java에서 int[] 같은 기본형 배열은 Comparator를 사용한 직접 정렬이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 아래와 같은 방식은 사용할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;Arrays.sort(numbers, (a, b) -&amp;gt; b - a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Comparator를 사용하려면 Integer[], String[], 또는 List&amp;lt;Integer&amp;gt; 같은 객체 형태를 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나는 int[]를 List&amp;lt;Integer&amp;gt;로 변환해서 풀었다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbersList = new ArrayList&amp;lt;&amp;gt;();

for (int item : numbers) {
    numbersList.add(item);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 배열 정렬과 리스트 정렬의 차이도 다시 정리할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;int[] 배열 기본 정렬 &amp;rarr; Arrays.sort(numbers)
int[] 배열은 Comparator 사용 불가
List&amp;lt;Integer&amp;gt;는 Comparator 사용 가능
Integer[] 또는 String[]도 Comparator 사용 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 두 숫자 a, b가 있을 때 어느 숫자가 앞에 와야 더 큰 수가 되는지 비교하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 a = 3, b = 30이라면,&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a + b = 330
b + a = 303
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 330이 더 크기 때문에 a가 b보다 앞에 와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 정렬 기준은 다음과 같이 잡을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;(String.valueOf(b) + String.valueOf(a))
    .compareTo(String.valueOf(a) + String.valueOf(b))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Integer.valueOf()로 숫자로 바꿔서 비교하려고 했는데, 이 방식은 위험할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 이어 붙인 값이 너무 커지면 Integer 범위를 넘어갈 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 최종 결과도 문자열로 반환하는 문제이므로, 굳이 숫자로 바꾸지 않고 문자열끼리 compareTo()로 비교하는 것이 더 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Comparator에서 헷갈렸던 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 Comparator가 아직 익숙하지 않다는 것을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Comparator는 정렬할 때 두 값을 비교해서 어떤 값이 앞에 올지 정하는 규칙이다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;list.sort((a, b) -&amp;gt; {
    return 비교결과;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 a, b가 인덱스가 아니라 리스트 안에 들어있는 실제 값이라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 부분이 헷갈려서 numbersList.get(a)처럼 생각했는데, a와 b는 이미 비교 대상 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 리스트에 아래 값들이 있을 때,&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[3, 30, 34, 5, 9]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 과정에서 a = 3, b = 30처럼 실제 값이 들어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 numbersList.get(a)처럼 접근하는 것이 아니라, a와 b를 그대로 사용해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 케이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의해야 할 예외 케이스는 모든 숫자가 0인 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 입력이 아래와 같을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[0, 0, 0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 이어 붙이면 결과는 &quot;000&quot;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문제에서 원하는 답은 &quot;0&quot;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정렬 후 만들어진 문자열의 첫 번째 문자가 '0'이라면, 가장 큰 수도 결국 0이라는 뜻이므로 &quot;0&quot;을 반환하도록 처리했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;return sb.charAt(0) == '0' ? &quot;0&quot; : sb.toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public String solution(int[] numbers) {
        List&amp;lt;Integer&amp;gt; numbersList = new ArrayList&amp;lt;&amp;gt;();

        for (int item : numbers) {
            numbersList.add(item);
        }

        numbersList.sort((a, b) -&amp;gt;
            (String.valueOf(b) + String.valueOf(a))
                .compareTo(String.valueOf(a) + String.valueOf(b))
        );

        StringBuilder sb = new StringBuilder();

        for (Integer number : numbersList) {
            sb.append(number);
        }

        return sb.charAt(0) == '0' ? &quot;0&quot; : sb.toString();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 풀이에서 가장 시간이 많이 걸리는 부분은 정렬이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;numbersList.sort((a, b) -&amp;gt;
    (String.valueOf(b) + String.valueOf(a))
        .compareTo(String.valueOf(a) + String.valueOf(b))
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 길이를 N이라고 하면 정렬의 시간 복잡도는 일반적으로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;O(N log N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Comparator 내부에서는 두 숫자를 문자열로 바꾼 뒤 이어 붙여 비교한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a + b
b + a
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 제한에서 numbers의 원소는 0 이상 1,000 이하이므로 숫자 하나의 길이는 최대 4자리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 문자열을 이어 붙이고 비교하는 비용은 크지 않으며 상수 시간처럼 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 전체 시간 복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;시간 복잡도: O(N log N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간은 크게 두 곳에서 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 int[] numbers를 List&amp;lt;Integer&amp;gt;로 옮기는 부분이다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbersList = new ArrayList&amp;lt;&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 배열의 길이가 N이라면, 리스트에도 N개의 값이 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 결과 문자열을 만들기 위한 StringBuilder이다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 결과 문자열은 모든 숫자를 이어 붙인 길이만큼 공간을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 추가로 사용하는 공간은 입력 크기에 비례한다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;공간 복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;복잡도 정리&lt;/h2&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;시간 복잡도: O(N log N)
공간 복잡도: O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 풀이에서 배운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 단순히 정렬만 하면 되는 문제처럼 보였지만, 실제로는 정렬 기준을 직접 만들어야 하는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 내용을 다시 정리할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;int[]는 Comparator 정렬이 불가능하다.
Comparator를 사용하려면 List&amp;lt;Integer&amp;gt;, Integer[], String[] 등을 사용해야 한다.
Comparator의 a, b는 인덱스가 아니라 실제 값이다.
문자열 비교는 compareTo()를 사용한다.
이 문제는 숫자 크기가 아니라 a+b, b+a를 비교해야 한다.
모든 값이 0인 경우에는 &quot;0&quot;을 반환해야 한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 숫자를 큰 순서대로 정렬하면 될 줄 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 3과 30 같은 예시를 보면서 단순 숫자 비교가 아니라, 두 숫자를 이어 붙였을 때 더 큰 조합이 되는 순서로 정렬해야 한다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 Comparator 사용이 아직 익숙하지 않다는 것도 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 여러 방식으로 시도하면서 배열 정렬과 리스트 정렬의 차이, 그리고 Comparator의 동작 방식을 조금 더 이해할 수 있었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셀프 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 처음에 바로 풀지 못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 자체의 핵심은 정렬이었지만, 일반적인 숫자 내림차순 정렬로 생각해서 처음 접근이 조금 막혔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 Java에서 Comparator를 사용하는 문법이 익숙하지 않아 여러 번 수정하면서 풀었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 해석을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 숫자 자체의 크기를 기준으로 정렬하면 된다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 문제는 숫자 크기가 아니라 두 숫자를 이어 붙였을 때 더 큰 조합이 되는 순서를 찾아야 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조 선택을 잘못했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 int[] 배열을 바로 Comparator로 정렬하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Java에서 int[]는 Comparator 정렬이 불가능하므로 List&amp;lt;Integer&amp;gt;로 변환해서 해결했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엣지 케이스를 놓쳤는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 값이 0인 경우를 주의해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[0, 0, 0]을 그냥 이어 붙이면 &quot;000&quot;이 되기 때문에, 첫 글자가 '0'이면 &quot;0&quot;을 반환하도록 처리했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 실수가 있었는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Comparator 문법에서 실수가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 a, b를 인덱스처럼 생각하거나, Integer.compare()와 compareTo() 사용법을 헷갈렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 풀이를 통해 Comparator는 두 값을 비교해서 정렬 순서를 결정하는 규칙이라는 점을 다시 정리할 수 있었다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/347</guid>
      <comments>https://mymelody.tistory.com/347#entry347comment</comments>
      <pubDate>Sun, 14 Jun 2026 20:30:01 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 연속된 부분 수열의 합 | 두 번째 풀이</title>
      <link>https://mymelody.tistory.com/346</link>
      <description>&lt;h1&gt;연속된 부분 수열의 합&amp;nbsp;|&amp;nbsp;두&amp;nbsp;번째&amp;nbsp;풀이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3주 전에 풀었던 프로그래머스 &lt;b&gt;연속된 부분 수열의 합&lt;/b&gt; 문제를 다시 풀어봤다.&lt;br /&gt;&lt;br /&gt;&lt;span&gt;이전에 풀었던 기록은 아래 글에 정리해두었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;a href=&quot;https://mymelody.tistory.com/327&quot;&gt;&lt;span&gt;프로그래머스 - 연속된 부분 수열의 합 &lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 풀었을 때도 투 포인터를 사용했지만, 그때는 for문 + while문 구조로 풀었다.&lt;br /&gt;이번에는 while문 하나를 사용해서 left와 right를 직접 움직이는 방식으로 다시 풀어봤다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 주어진 수열 sequence에서 합이 k가 되는 &lt;b&gt;연속된 부분 수열&lt;/b&gt;을 찾는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 연속된 구간의 합이 k가 되어야 한다.
2. 합이 k인 구간이 여러 개라면 길이가 가장 짧은 구간을 선택한다.
3. 길이가 같다면 시작 인덱스가 더 작은 구간을 선택한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sequence = [1, 2, 3, 4, 5]
k = 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라면 합이 7이 되는 연속 구간은 [3, 4]이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스로 보면 2부터 3까지이므로 정답은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://mymelody.tistory.com/327&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 풀이&lt;/a&gt;와 이번 풀이의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 풀었을 때는 for문으로 right를 하나씩 증가시키고, 합이 k보다 커졌을 때 while문으로 left를 움직이는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 이런 느낌이다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int left = 0;
int sum = 0;

for (int right = 0; right &amp;lt; sequence.length; right++) {
    sum += sequence[right];

    while (sum &amp;gt; k) {
        sum -= sequence[left];
        left++;
    }

    if (sum == k) {
        // 정답 후보 저장
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식에서는 right가 for문에 의해 자동으로 증가한다.&lt;br /&gt;그리고 left는 합이 커졌을 때만 while문 안에서 움직인다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 풀이에서는 while문 하나 안에서 현재 합의 상태를 보고 left 또는 right를 직접 움직였다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;sum &amp;lt; k  &amp;rarr; right 증가
sum &amp;gt; k  &amp;rarr; left 증가
sum == k &amp;rarr; 정답 후보 저장 후 left 증가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 투 포인터 풀이지만, 이번 풀이가 포인터의 움직임을 더 직접적으로 볼 수 있어서 좋았다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;

&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;투 포인터로 접근한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 연속된 구간의 합을 찾는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 생각하면 시작점을 하나씩 정하고, 끝점을 늘려가며 모든 구간의 합을 확인할 수도 있다.&lt;br /&gt;하지만 그렇게 하면 확인해야 하는 구간이 많아져서 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 수열이 &lt;b&gt;비내림차순&lt;/b&gt;이라는 특징이 있다.&lt;br /&gt;그래서 현재 구간의 합이 작으면 오른쪽으로 구간을 넓히고, 합이 크면 왼쪽을 줄이는 방식이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 현재 합을 기준으로 구간을 조절할 수 있기 때문에 투 포인터를 사용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left는 현재 구간의 시작 인덱스,&lt;br /&gt;right는 현재 구간의 끝 인덱스로 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;b&gt;[0, 0]&lt;/b&gt; 구간에서 시작한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int left = 0;
int right = 0;
int sum = sequence[0];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구간은 sequence[left]부터 sequence[right]까지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 배열이 있다면,&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[1, 2, 3, 4, 5]
 L
 R
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구간은 [1]이고, 합은 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 합이 k보다 작으면 오른쪽을 늘린다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[1, 2, 3, 4, 5]
 L  R
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 합이 k보다 크면 왼쪽 값을 빼고 left를 이동한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[1, 2, 3, 4, 5]
    L     R
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 현재 합에 따라 구간을 넓히거나 줄이면서 답을 찾는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음에 헷갈렸던 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 반복문 안에서 계속 sum += sequence[right]를 해주려고 했다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;sum += sequence[right];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이렇게 하면 right가 움직이지 않았는데도 같은 값을 또 더할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투 포인터에서는 sum을 매번 새로 계산하는 것이 아니라, 포인터가 움직일 때만 합을 갱신해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;right가 움직이면 새로 포함된 값을 sum에 더한다.
left가 움직이면 빠지는 값을 sum에서 뺀다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 이해하고 나니 코드 흐름이 훨씬 깔끔해졌다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 합이 k와 같은 경우에는 정답 후보가 된다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;if (sum == k) {
    int length = right - left + 1;

    if (length &amp;lt; minLength || 
        (length == minLength &amp;amp;&amp;amp; left &amp;lt; answer[0])) {
        minLength = length;
        answer[0] = left;
        answer[1] = right;
    }

    sum -= sequence[left];
    left++;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답을 찾았다고 바로 멈추면 안 된다.&lt;br /&gt;뒤쪽에 더 짧은 구간이 나올 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 정답 후보를 저장한 뒤, 왼쪽 값을 빼고 다음 구간을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 합이 k보다 크면 구간을 줄여야 한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;else if (sum &amp;gt; k) {
    sum -= sequence[left];
    left++;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 합이 k보다 작으면 오른쪽으로 구간을 넓힌다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;else {
    right++;

    if (right &amp;lt; sequence.length) {
        sum += sequence[right];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 right가 배열 범위를 벗어나지 않도록 조건을 확인해주었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;길이가 같은 경우 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 조건에는 길이가 같다면 시작 인덱스가 작은 구간을 선택하라고 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 조건을 명확하게 코드로 표현하면 이렇게 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;if (length &amp;lt; minLength || 
    (length == minLength &amp;amp;&amp;amp; left &amp;lt; answer[0])) {
    minLength = length;
    answer[0] = left;
    answer[1] = right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건의 의미는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 구간이 더 짧으면 정답 갱신
또는
길이가 같고 시작 인덱스가 더 작으면 정답 갱신
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사실 이 문제에서는 투 포인터가 앞에서부터 탐색되기 때문에, 같은 길이의 구간이 나중에 나온다면 시작 인덱스는 더 뒤에 있다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그래서 실제로는 아래처럼 더 짧은 경우에만 갱신해도 통과할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;if (length &amp;lt; minLength) {
    minLength = length;
    answer[0] = left;
    answer[1] = right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문제 조건을 코드에 더 명확하게 드러내고 싶다면, 길이가 같은 경우까지 포함한 조건문을 작성하는 것도 괜찮다고 생각했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;class Solution {
    public int[] solution(int[] sequence, int k) {
        int left = 0;
        int right = 0;
        int sum = sequence[0];

        int[] answer = new int[2];
        int minLength = Integer.MAX_VALUE;

        while (left &amp;lt; sequence.length &amp;amp;&amp;amp; right &amp;lt; sequence.length) {
            if (sum == k) {
                int length = right - left + 1;

                if (length &amp;lt; minLength || 
                    (length == minLength &amp;amp;&amp;amp; left &amp;lt; answer[0])) {
                    minLength = length;
                    answer[0] = left;
                    answer[1] = right;
                }

                sum -= sequence[left];
                left++;

            } else if (sum &amp;gt; k) {
                sum -= sequence[left];
                left++;

            } else {
                right++;

                if (right &amp;lt; sequence.length) {
                    sum += sequence[right];
                }
            }
        }

        return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left와 right는 각각 배열의 끝까지 한 번씩만 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 시간 복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 사용하는 변수도 많지 않기 때문에 공간 복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 예전에 풀었던 문제를 약 3주 만에 다시 풀어본 거라서 더 의미가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 풀었을 때는 for문 + while문으로 투 포인터를 구현했다.&lt;br /&gt;이번에는 while문 하나로 현재 합에 따라 left와 right를 직접 움직이는 방식으로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 투 포인터 풀이지만, 이번 방식은 포인터가 왜 움직이는지 더 잘 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 핵심은 이것이다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;합이 작으면 right 증가
합이 크면 left 증가
합이 같으면 정답 저장 후 left 증가
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 sum은 반복문마다 무조건 더하는 것이 아니라, 포인터가 움직일 때만 더하거나 빼야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 문제를 다시 풀어보니, 예전에는 그냥 코드 흐름을 따라가며 풀었다면 이번에는 투 포인터가 움직이는 이유를 조금 더 이해하게 된 것 같다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/346</guid>
      <comments>https://mymelody.tistory.com/346#entry346comment</comments>
      <pubDate>Sun, 14 Jun 2026 18:14:58 +0900</pubDate>
    </item>
    <item>
      <title>이분 탐색(Binary Search), 정렬된 데이터에서 절반씩 줄여 찾기</title>
      <link>https://mymelody.tistory.com/344</link>
      <description>&lt;h1&gt;이분 탐색&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 영어로 Binary Search라고 한다.&lt;br /&gt;처음에는 이름이 조금 어렵게 느껴졌는데, 개념 자체는 생각보다 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 이것이다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;정렬된 데이터에서 가운데 값을 확인하고,
필요 없는 절반을 버리면서 원하는 값을 찾는 방법이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 처음부터 끝까지 하나씩 확인하는 것이 아니라,&lt;br /&gt;범위를 계속 절반으로 줄여가며 찾는 탐색 방법이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 &lt;b&gt;정렬된 배열에서 원하는 값을 빠르게 찾는 알고리즘&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 1부터 100까지 숫자 중에서 73을 찾는다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 찾는다면 이렇게 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1, 2, 3, 4, 5, ... 73
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운이 나쁘면 꽤 많은 숫자를 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이분 탐색은 가운데부터 본다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1 ~ 100의 가운데쯤인 50을 확인한다.
73은 50보다 크다.
그러면 1 ~ 50은 볼 필요가 없다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 범위는 51 ~ 100으로 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 가운데를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;51 ~ 100의 가운데쯤인 75를 확인한다.
73은 75보다 작다.
그러면 76 ~ 100은 볼 필요가 없다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 계속 범위를 절반씩 줄여가며 원하는 값을 찾는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색의 핵심 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색에서 가장 중요한 조건은 &lt;b&gt;데이터가 &lt;span style=&quot;color: #ee2323;&quot;&gt;정렬&lt;/span&gt;되어 있어야 한다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 배열은 이분 탐색이 가능하다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] arr = {1, 3, 5, 7, 9, 11};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오름차순으로 정렬되어 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아래 배열은 바로 이분 탐색을 하기 어렵다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] arr = {7, 1, 11, 3, 9, 5};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬되어 있지 않으면 가운데 값을 보고 왼쪽으로 갈지, 오른쪽으로 갈지 판단할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 가운데 값을 기준으로 이렇게 판단한다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;target이 mid 값보다 크다  &amp;rarr; 오른쪽 탐색
target이 mid 값보다 작다 &amp;rarr; 왼쪽 탐색
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 판단이 가능하려면 배열이 정렬되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이분 탐색을 하기 전에는 보통 정렬을 먼저 한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Arrays.sort(arr);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;순차 탐색과 이분 탐색 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색을 이해하려면 순차 탐색과 비교해보면 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분순차 탐색이분 탐색&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;탐색 방식&lt;/td&gt;
&lt;td&gt;앞에서부터 하나씩 확인&lt;/td&gt;
&lt;td&gt;가운데부터 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;조건&lt;/td&gt;
&lt;td&gt;정렬 필요 없음&lt;/td&gt;
&lt;td&gt;정렬 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;범위 줄이기&lt;/td&gt;
&lt;td&gt;한 칸씩 이동&lt;/td&gt;
&lt;td&gt;절반씩 줄임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시간복잡도&lt;/td&gt;
&lt;td&gt;O(N)&lt;/td&gt;
&lt;td&gt;O(log N)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차 탐색은 단순하지만 데이터가 많아지면 오래 걸릴 수 있다.&lt;br /&gt;반면 이분 탐색은 정렬되어 있다는 조건만 만족하면 훨씬 빠르게 찾을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색에서 사용하는 변수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색에서는 보통 세 가지 변수를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;left&lt;/td&gt;
&lt;td&gt;현재 탐색 범위의 시작 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;right&lt;/td&gt;
&lt;td&gt;현재 탐색 범위의 끝 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mid&lt;/td&gt;
&lt;td&gt;현재 탐색 범위의 가운데 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 배열이 다음과 같다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] arr = {1, 3, 5, 7, 9, 11};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 배열 전체를 탐색 범위로 잡는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int left = 0;
int right = arr.length - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 인덱스는 이렇게 구한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래처럼 생각하기 쉬웠다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = (left + right) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 숫자에서는 두 코드의 결과가 같다.&lt;br /&gt;하지만 &lt;b&gt;left와 right가 아주 큰 경우에는 left + right가 int 범위를 넘어 오버플로우가 날 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 더 &lt;b&gt;안전하게 쓰려면 아래 방식을 사용하는 것이 좋다&lt;/b&gt;.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 left와 right를 먼저 더하지 않고,&lt;br /&gt;right - left로 거리의 절반을 구한 뒤 left에 더하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 같은 가운데 값을 구하지만 더 안전한 표현이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾고 싶은 값이 9라고 해보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] arr = {1, 3, 5, 7, 9, 11};
int target = 9;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 범위는 전체 배열이다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = 0
right = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데를 구하면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;mid = 2
arr[mid] = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target인 9는 5보다 크다.&lt;br /&gt;그러면 5보다 왼쪽에 있는 값들은 볼 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 left를 이동시킨다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 탐색 범위는 오른쪽으로 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = 3
right = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 가운데를 구한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;mid = 4
arr[mid] = 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target과 같으므로 값을 찾은 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Java 코드로 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색 기본 코드는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 11};
        int target = 9;

        int left = 0;
        int right = arr.length - 1;

        boolean found = false;

        while (left &amp;lt;= right) {
            int mid = left + (right - left) / 2;

            if (arr[mid] == target) {
                found = true;
                break;
            } else if (arr[mid] &amp;lt; target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        System.out.println(found);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 해석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 가운데 값을 확인한다&lt;/h3&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 탐색 범위에서 가운데 인덱스를 구한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 값을 찾은 경우&lt;/h3&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;if (arr[mid] == target) {
    found = true;
    break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값이 찾는 값과 같으면 탐색을 종료한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. target이 더 큰 경우&lt;/h3&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;else if (arr[mid] &amp;lt; target) {
    left = mid + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값보다 target이 크다면 오른쪽에 있을 가능성이 있다.&lt;br /&gt;그래서 왼쪽 범위는 버리고 left를 이동시킨다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. target이 더 작은 경우&lt;/h3&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;else {
    right = mid - 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값보다 target이 작다면 왼쪽에 있을 가능성이 있다.&lt;br /&gt;그래서 오른쪽 범위는 버리고 right를 이동시킨다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 left &amp;lt;= right를 사용할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색의 반복문은 보통 이렇게 작성한다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;while (left &amp;lt;= right)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 left &amp;lt; right와 헷갈릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left와 right가 같은 경우는 탐색할 값이 딱 하나 남은 상태이다.&lt;br /&gt;그 하나도 확인해야 하므로 left &amp;lt;= right를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = 3
right = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이면 아직 3번 인덱스 값을 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이분 탐색 기본 형태에서는 left &amp;lt;= right를 많이 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;범위 갱신에서 mid + 1, mid - 1을 쓰는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값 arr[mid]는 이미 확인한 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음 탐색 범위에서는 다시 볼 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target이 더 크다면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target이 더 작다면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;right = mid - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 가운데 인덱스를 제외하고 범위를 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 아래처럼 작성하면 문제가 생길 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = mid;
right = mid;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 같은 mid를 계속 확인하게 되어 무한 반복이 생길 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 한 번 확인할 때마다 탐색 범위가 절반으로 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 데이터가 100개라면 이런 식으로 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;100개
&amp;rarr; 50개
&amp;rarr; 25개
&amp;rarr; 12개
&amp;rarr; 6개
&amp;rarr; 3개
&amp;rarr; 1개
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 시간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(log N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차 탐색이 O(N)인 것과 비교하면 훨씬 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색 방법시간복잡도&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;순차 탐색&lt;/td&gt;
&lt;td&gt;O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이분 탐색&lt;/td&gt;
&lt;td&gt;O(log N)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 많아질수록 이 차이는 더 커진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문으로 이분 탐색을 구현하면 추가로 큰 배열을 만들지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left, right, mid 같은 변수만 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색에서 자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 정렬하지 않고 이분 탐색하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 정렬된 데이터에서만 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬이 되어 있지 않으면 가운데 값을 기준으로 왼쪽/오른쪽을 판단할 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. while 조건 실수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 이분 탐색에서는 보통 아래 조건을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;while (left &amp;lt;= right)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 하나 남은 경우까지 확인해야 하기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 범위 갱신 실수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가운데 값은 이미 확인했으므로 다음 범위에서 제외해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left = mid + 1;
right = mid - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 잘못 쓰면 무한 반복이 생길 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. mid 계산 오버플로우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 방식은 작은 숫자에서는 괜찮다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = (left + right) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 left와 right가 매우 클 경우 left + right에서 오버플로우가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아래 방식을 습관화하면 좋다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;int mid = left + (right - left) / 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이분 탐색은 언제 떠올리면 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 이런 느낌이 나오면 이분 탐색을 의심해볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;정렬된 배열에서 특정 값을 찾는다.
탐색 범위가 매우 크다.
하나씩 확인하면 시간이 부족하다.
조건을 만족하는 최소값을 찾아야 한다.
조건을 만족하는 최대값을 찾아야 한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 배열에서 target을 찾는 문제부터 연습하면 좋다.&lt;br /&gt;이후에는 &amp;ldquo;정답의 범위&amp;rdquo;를 두고 이분 탐색하는 문제로 넘어가면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 문제들이 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;특정 값이 배열에 있는지 찾기
조건을 만족하는 가장 작은 값 찾기
조건을 만족하는 가장 큰 값 찾기
공유기 설치
입국심사
예산
랜선 자르기
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제들은 단순히 값이 배열 안에 있는지 찾는 것보다 조금 더 어렵다.&lt;br /&gt;하지만 핵심은 같다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;가운데 값을 기준으로 가능/불가능을 판단하고,
필요 없는 절반을 버린다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 이해한 이분 탐색 핵심&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 이분 탐색을 공부하면서 가장 중요하게 느낀 점은 이것이다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이분 탐색은 정렬된 상태에서만 가능하다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬되어 있어야 가운데 값을 기준으로 왼쪽을 버릴지, 오른쪽을 버릴지 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 실제 코드에서는 세 가지 변수를 잘 관리해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;left  = 탐색 범위의 시작
right = 탐색 범위의 끝
mid   = 탐색 범위의 가운데
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색 흐름은 이렇게 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 가운데 값을 확인한다.
2. target과 비교한다.
3. target이 더 크면 오른쪽만 본다.
4. target이 더 작으면 왼쪽만 본다.
5. 찾을 때까지 반복한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 이름만 보면 어렵게 느껴지지만, 결국 핵심은 단순하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;정렬된 데이터에서 가운데를 확인하고,
필요 없는 절반을 버리면서 찾는 방법이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 전부 확인하는 것보다 훨씬 빠르기 때문에,&lt;br /&gt;&lt;b&gt;데이터가 많고 정렬이 가능하거나 이미 정렬되어 있는 문제&lt;/b&gt;에서 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 이분 탐색 문제를 풀 때는 먼저 아래 질문을 해봐야겠다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 데이터가 정렬되어 있는가?
2. 가운데 값을 기준으로 왼쪽/오른쪽을 버릴 수 있는가?
3. 하나씩 확인하면 시간이 너무 오래 걸리는가?
4. 조건을 만족하는 최소값 또는 최대값을 찾는 문제인가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 해당한다면 이분 탐색을 떠올려볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 구현할 때는 아래 세 가지를 조심해야겠다.&lt;/p&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;while 조건은 left &amp;lt;= right
범위 갱신은 mid + 1, mid - 1
mid 계산은 left + (right - left) / 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이분 탐색은 단순한 값 찾기부터 시작해서, 나중에는 정답의 범위를 줄여가는 문제까지 확장된다.&lt;br /&gt;이번에는 기본 개념을 정리했으니, 다음에는 직접 문제를 풀면서 left, right, mid를 어떻게 잡는지 연습해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpmfHX/dJMcagFLm8B/KYvYk2ZIkI5zuD8KY51BeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpmfHX/dJMcagFLm8B/KYvYk2ZIkI5zuD8KY51BeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpmfHX/dJMcagFLm8B/KYvYk2ZIkI5zuD8KY51BeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpmfHX%2FdJMcagFLm8B%2FKYvYk2ZIkI5zuD8KY51BeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;1086&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>CS/알고리즘</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/344</guid>
      <comments>https://mymelody.tistory.com/344#entry344comment</comments>
      <pubDate>Mon, 1 Jun 2026 19:50:42 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 체육복</title>
      <link>https://mymelody.tistory.com/343</link>
      <description>&lt;h1&gt;프로그래머스 체육복 Java 풀이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 프로그래머스 그리디 유형의 &lt;b&gt;체육복&lt;/b&gt; 문제를 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복 문제는 그리디 입문 문제로 많이 언급되는 문제이다.&lt;br /&gt;문제 자체는 어렵게 느껴지지 않았지만, 실제로 풀면서 연산자 실수 때문에 잠깐 헤맸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 +=를 써야 하는 부분에서 =+처럼 작성하면서 값이 예상과 다르게 나왔고, 디버깅하여 실수를 확인하고 수정하였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 정보&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문제&lt;/td&gt;
&lt;td&gt;프로그래머스 체육복&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유형&lt;/td&gt;
&lt;td&gt;그리디&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 언어&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;핵심 자료구조&lt;/td&gt;
&lt;td&gt;int 배열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 개념&lt;/td&gt;
&lt;td&gt;학생별 체육복 개수 관리, 앞뒤 학생에게 빌려주기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시간복잡도&lt;/td&gt;
&lt;td&gt;O(N log N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공간복잡도&lt;/td&gt;
&lt;td&gt;O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;풀이시간&lt;/td&gt;
&lt;td&gt;40분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결과&lt;/td&gt;
&lt;td&gt;통과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 학생 수 n이 주어진다.&lt;br /&gt;일부 학생은 체육복을 도난당했고, 일부 학생은 여벌 체육복을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복을 도난당한 학생은 수업을 들을 수 없지만, 바로 앞번호나 바로 뒷번호 학생에게 여벌 체육복을 빌릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, i번 학생이 여벌 체육복을 가지고 있다면 다음 학생에게만 빌려줄 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;i - 1번 학생
i + 1번 학생
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 &lt;b&gt;체육수업을 들을 수 있는 학생 수의 최댓값&lt;/b&gt;을 구하는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입출력 예시로 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 입력이 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;n = 5
lost = [2, 4]
reserve = [1, 3, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생은 총 5명이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 번호상태&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;여벌 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;도난당함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;여벌 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;도난당함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;여벌 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 학생은 2번 학생에게 체육복을 빌려줄 수 있다.&lt;br /&gt;3번 학생은 4번 학생에게 체육복을 빌려줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 모든 학생이 체육수업을 들을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제를 보고 떠올린 생각&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 각 학생의 체육복 개수를 배열로 관리하면 되겠다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생은 기본적으로 체육복을 1벌 가지고 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기본 상태: 1벌
여벌이 있으면: +1
도난당했으면: -1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 학생별 체육복 상태를 다음과 같이 표현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;체육복이 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;체육복이 1벌 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;여벌 체육복까지 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 lost와 reserve에 동시에 포함된 학생도 자연스럽게 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 어떤 학생이 여벌 체육복도 있고 도난도 당했다면:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기본값 1
여벌 +1 &amp;rarr; 2
도난 -1 &amp;rarr; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 본인이 입을 체육복 1벌만 남게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 흐름은 다음과 같이 잡았다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 학생 수보다 1 큰 배열을 만든다.
2. 모든 학생의 체육복 개수를 1로 초기화한다.
3. reserve 학생은 +1 한다.
4. lost 학생은 -1 한다.
5. 왼쪽부터 차례대로 확인하면서 여벌이 있는 학생이 앞번호 또는 뒷번호 학생에게 빌려준다.
6. 체육복 개수가 1 이상인 학생 수를 센다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 번호가 1번부터 시작하므로 배열 크기는 n + 1로 만들었다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] student_clothes = new int[n + 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번 인덱스는 사용하지 않고, 1번 학생부터 n번 학생까지 사용했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내가 작성한 풀이&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int[] student_clothes = new int[n + 1];

        for (int i = 1; i &amp;lt; student_clothes.length; i++) {
            student_clothes[i] = 1;
        }

        Arrays.sort(reserve);
        Arrays.sort(lost);

        for (int i : reserve) {
            student_clothes[i] += 1;
        }

        for (int i : lost) {
            student_clothes[i] -= 1;
        }

        for (int i = 1; i &amp;lt; student_clothes.length; i++) {
            if (student_clothes[i] &amp;gt; 1) {
                if (i - 1 &amp;gt; 0 &amp;amp;&amp;amp; student_clothes[i - 1] == 0) {
                    student_clothes[i - 1] += 1;
                    student_clothes[i] -= 1;
                } else if (i + 1 &amp;lt; student_clothes.length &amp;amp;&amp;amp; student_clothes[i + 1] == 0) {
                    student_clothes[i + 1] += 1;
                    student_clothes[i] -= 1;
                }
            }
        }

        int answer = 0;

        for (int item : student_clothes) {
            if (item &amp;gt; 0) {
                answer++;
            }
        }

        return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그리디로 생각한 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서 그리디하게 생각한 부분은 &lt;b&gt;여벌 체육복이 있는 학생이 빌려줄 수 있는 학생에게 바로 빌려주는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 학생 번호를 왼쪽부터 차례대로 확인했다.&lt;br /&gt;그리고 여벌이 있는 학생을 만나면 먼저 앞번호 학생을 확인했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;if (i - 1 &amp;gt; 0 &amp;amp;&amp;amp; student_clothes[i - 1] == 0) {
    student_clothes[i - 1] += 1;
    student_clothes[i] -= 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞번호 학생에게 빌려줄 수 없다면 뒷번호 학생을 확인했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;else if (i + 1 &amp;lt; student_clothes.length &amp;amp;&amp;amp; student_clothes[i + 1] == 0) {
    student_clothes[i + 1] += 1;
    student_clothes[i] -= 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왼쪽부터 순서대로 확인할 때는 앞번호 학생을 먼저 챙기는 방식이 자연스럽다고 생각했다.&lt;/b&gt;&lt;br /&gt;이미 지나간 학생은 다시 확인하지 않기 때문에, 현재 학생이 앞번호 학생을 도울 수 있다면 먼저 도와주는 것이 좋다고 판단했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실수한 부분: +=를 =+로 작성함&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 풀이에서 헤맸던 부분은 연산자 실수였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복 개수를 1 증가시키려면 아래처럼 작성해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;student_clothes[i] += 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 처음에 실수로 아래처럼 작성했다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;student_clothes[i] =+ 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 비슷해 보여서 바로 눈에 들어오지 않았다.&lt;br /&gt;하지만 두 코드는 의미가 완전히 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x += 1&lt;/td&gt;
&lt;td&gt;기존 값에 1을 더한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x =+ 1&lt;/td&gt;
&lt;td&gt;+1을 대입한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x -= 1&lt;/td&gt;
&lt;td&gt;기존 값에서 1을 뺀다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x =- 1&lt;/td&gt;
&lt;td&gt;-1을 대입한다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, +=는 누적 연산이고, =+는 양수 값을 대입하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 =- 1처럼 작성하면 기존 값에서 1을 빼는 것이 아니라, 아예 -1을 대입하는 의미가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 값이 이상하게 -1로 나와서 디버깅하다가 이 부분을 발견했다.&lt;br /&gt;앞으로 누적 연산을 사용할 때는 +=, -= 방향을 꼭 확인해야겠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 풀이에서는 reserve와 lost 배열을 정렬했다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Arrays.sort(reserve);
Arrays.sort(lost);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬이 들어가기 때문에 시간복잡도는 다음과 같이 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;O(R log R + L log L + N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 R은 reserve 배열의 길이이고, L은 lost 배열의 길이이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기호의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;전체 학생 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R&lt;/td&gt;
&lt;td&gt;reserve 배열 길이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L&lt;/td&gt;
&lt;td&gt;lost 배열 길이&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reserve와 lost의 길이는 최대 N까지 가능하므로, 전체적으로는 다음처럼 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;O(N log N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각한 것처럼 정렬이 들어가므로 &lt;b&gt;O(N log N)&lt;/b&gt; 으로 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 정렬을 제외한 반복문은 대부분 한 번씩 순회하는 구조라 O(N)이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공간복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생별 체육복 개수를 저장하기 위해 n + 1 크기의 배열을 사용했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] student_clothes = new int[n + 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공간복잡도는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 수만큼 배열을 추가로 사용했기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;엣지 케이스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체육복 문제에서 조심해야 하는 케이스는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이스설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;잃어버렸지만 여벌도 있는 학생&lt;/td&gt;
&lt;td&gt;결과적으로 본인 체육복 1벌만 남음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1번 학생&lt;/td&gt;
&lt;td&gt;앞번호 학생이 없으므로 i - 1 &amp;gt; 0 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n번 학생&lt;/td&gt;
&lt;td&gt;뒷번호 학생이 없으므로 i + 1 &amp;lt; length 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;앞뒤 모두 빌려줄 수 있는 경우&lt;/td&gt;
&lt;td&gt;어떤 방향을 먼저 볼지 기준 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;아무도 빌려줄 수 없는 경우&lt;/td&gt;
&lt;td&gt;체육복 0인 학생은 수업 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 풀이에서는 학생별 체육복 개수를 배열로 관리했기 때문에,&lt;br /&gt;lost와 reserve에 동시에 포함된 학생도 자연스럽게 처리됐다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다른 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른&amp;nbsp;풀이를&amp;nbsp;보면서&amp;nbsp;인상적이었던&amp;nbsp;부분은&amp;nbsp;`Math.abs(lost[i]&amp;nbsp;-&amp;nbsp;reserve[j])&amp;nbsp;==&amp;nbsp;1`&amp;nbsp;조건이었다.&lt;br /&gt;&lt;br /&gt;처음에는&amp;nbsp;앞번호&amp;nbsp;학생과&amp;nbsp;뒷번호&amp;nbsp;학생을&amp;nbsp;각각&amp;nbsp;`i&amp;nbsp;-&amp;nbsp;1`,&amp;nbsp;`i&amp;nbsp;+&amp;nbsp;1`로&amp;nbsp;나누어&amp;nbsp;확인했다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;그런데&amp;nbsp;문제&amp;nbsp;조건을&amp;nbsp;다시&amp;nbsp;보면&amp;nbsp;체육복은&amp;nbsp;바로&amp;nbsp;앞번호&amp;nbsp;또는&amp;nbsp;바로&amp;nbsp;뒷번호&amp;nbsp;학생에게만&amp;nbsp;빌려줄&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;즉,&amp;nbsp;두&amp;nbsp;학생&amp;nbsp;번호의&amp;nbsp;차이가&amp;nbsp;1이면&amp;nbsp;빌려줄&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;뜻이다.&lt;br /&gt;&lt;br /&gt;그래서&amp;nbsp;`Math.abs()`를&amp;nbsp;사용하면&amp;nbsp;앞번호인지&amp;nbsp;뒷번호인지&amp;nbsp;따로&amp;nbsp;나누지&amp;nbsp;않고,&amp;nbsp;두&amp;nbsp;학생&amp;nbsp;번호의&amp;nbsp;차이만으로&amp;nbsp;빌릴&amp;nbsp;수&amp;nbsp;있는지&amp;nbsp;판단할&amp;nbsp;수&amp;nbsp;있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1780308616489&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (Math.abs(lost[i] - reserve[j]) == 1) {
    reserve[j] = -1;
    answer++;
    break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;greedy.png&quot; data-origin-width=&quot;1535&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tyD8J/dJMb99T7ges/jlgY7IUKaZFkddKBiETKHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tyD8J/dJMb99T7ges/jlgY7IUKaZFkddKBiETKHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tyD8J/dJMb99T7ges/jlgY7IUKaZFkddKBiETKHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtyD8J%2FdJMb99T7ges%2FjlgY7IUKaZFkddKBiETKHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1535&quot; height=&quot;1024&quot; data-filename=&quot;greedy.png&quot; data-origin-width=&quot;1535&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;더 간단한 풀이&lt;/h2&gt;
&lt;pre id=&quot;code_1780308685336&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        Arrays.sort(lost);
        Arrays.sort(reserve);

        int answer = n - lost.length;

        // 1. lost와 reserve에 둘 다 있는 학생 처리
        for (int i = 0; i &amp;lt; lost.length; i++) {
            for (int j = 0; j &amp;lt; reserve.length; j++) {
                if (lost[i] == reserve[j]) {
                    lost[i] = -1;
                    reserve[j] = -1;
                    answer++;
                    break;
                }
            }
        }

        // 2. 여벌 체육복 빌려주기
        for (int i = 0; i &amp;lt; lost.length; i++) {
            if (lost[i] == -1) continue;

            for (int j = 0; j &amp;lt; reserve.length; j++) {
                if (reserve[j] == -1) continue;

                if (Math.abs(lost[i] - reserve[j]) == 1) {
                    reserve[j] = -1;
                    answer++;
                    break;
                }
            }
        }

        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8230979612679494&quot;
     crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;ins class=&quot;adsbygoogle&quot;
     style=&quot;display:block; text-align:center;&quot;
     data-ad-layout=&quot;in-article&quot;
     data-ad-format=&quot;fluid&quot;
     data-ad-client=&quot;ca-pub-8230979612679494&quot;
     data-ad-slot=&quot;1571747719&quot;&gt;&lt;/ins&gt;
&lt;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번 문제 회고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;풀었는가?&lt;/td&gt;
&lt;td&gt;풀이 완료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문제 해석&lt;/td&gt;
&lt;td&gt;학생별 체육복 수를 관리하면 된다고 이해함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자료구조 선택&lt;/td&gt;
&lt;td&gt;int 배열 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;그리디 기준&lt;/td&gt;
&lt;td&gt;앞번호 학생을 먼저 확인하고, 불가능하면 뒷번호 학생 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 실수&lt;/td&gt;
&lt;td&gt;+=를 =+로 잘못 작성함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;디버깅 포인트&lt;/td&gt;
&lt;td&gt;값이 -1로 나와 연산자 실수를 발견함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시간복잡도 판단&lt;/td&gt;
&lt;td&gt;정렬 때문에 O(N log N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공간복잡도 판단&lt;/td&gt;
&lt;td&gt;학생 수만큼 배열을 사용하므로 O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다시 풀 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 풀 때는 아래 부분을 주의해야겠다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. lost와 reserve에 동시에 있는 학생을 먼저 고려한다.
2. 학생 번호는 1번부터 시작하므로 배열 인덱스를 조심한다.
3. 1번 학생과 n번 학생은 앞뒤 범위 체크를 해야 한다.
4. 왼쪽부터 확인할 때는 앞번호 학생을 먼저 챙기는 기준을 명확히 한다.
5. +=와 =+를 헷갈리지 않는다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이번 문제에서는 알고리즘 자체보다 연산자 실수 때문에 시간이 더 걸렸다.&lt;br /&gt;값이 이상하게 고정되거나 -1이 나온다면, +=, -=, =+, =-를 먼저 확인해봐야겠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 체육복 문제는 그리디 입문 문제로 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 자체는 &amp;ldquo;여벌이 있는 학생이 앞뒤 학생에게 체육복을 빌려준다&amp;rdquo;는 단순한 구조였지만,&lt;br /&gt;실제로 코드로 구현할 때는 학생별 상태를 어떻게 관리할지, 어떤 순서로 빌려줄지 정해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 풀이에서는 student_clothes 배열을 사용해서 학생별 체육복 개수를 관리했고,&lt;br /&gt;왼쪽부터 순회하면서 여벌이 있는 학생이 앞번호 학생을 먼저 도와주는 방식으로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서 가장 크게 배운 점은 두 가지이다.&lt;/p&gt;
&lt;pre class=&quot;mercury&quot;&gt;&lt;code&gt;첫째, 그리디 문제는 선택 기준을 명확히 해야 한다.
둘째, +=와 =+는 완전히 다르다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 그리디 문제를 풀 때는 먼저 &amp;ldquo;무엇을 기준으로 선택할 것인가&amp;rdquo;를 정리하고,&lt;br /&gt;구현할 때는 단순한 연산자 실수도 꼼꼼히 확인해야겠다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/343</guid>
      <comments>https://mymelody.tistory.com/343#entry343comment</comments>
      <pubDate>Mon, 1 Jun 2026 19:27:05 +0900</pubDate>
    </item>
  </channel>
</rss>