<?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>Thu, 18 Jun 2026 23:46:13 +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/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>
    <item>
      <title>그리디, 빠르게 고르지만 신중해야 하는 이유</title>
      <link>https://mymelody.tistory.com/342</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;br /&gt;영어로 Greedy는 &amp;ldquo;탐욕스러운&amp;rdquo;이라는 뜻인데, 알고리즘에서는 &lt;b&gt;매 순간 가장 좋아 보이는 선택을 하는 방법&lt;/b&gt;을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &amp;ldquo;그냥 제일 큰 값이나 제일 작은 값을 고르면 되는 건가?&amp;rdquo;라고 생각했는데, 공부해보니 그렇게 단순하게만 볼 문제는 아니었다.&lt;br /&gt;그리디에서 중요한 것은 &lt;b&gt;지금 선택한 것이 나중에도 손해가 되지 않는지&lt;/b&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;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;760원을 거슬러줘야 하고, 동전이 다음과 같이 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;500원, 100원, 50원, 10원
&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;760원
&amp;rarr; 500원 1개 사용, 남은 금액 260원
&amp;rarr; 100원 2개 사용, 남은 금액 60원
&amp;rarr; 50원 1개 사용, 남은 금액 10원
&amp;rarr; 10원 1개 사용, 남은 금액 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;동전개수&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;500원&lt;/td&gt;
&lt;td&gt;1개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100원&lt;/td&gt;
&lt;td&gt;2개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50원&lt;/td&gt;
&lt;td&gt;1개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10원&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;5개&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;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;그리디의 핵심은 &amp;ldquo;지금 이 선택을 해도 괜찮은가?&amp;rdquo;이다&lt;/h2&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;p data-ke-size=&quot;size16&quot;&gt;예를 들어 동전이 이렇게 있다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;500원, 400원, 100원
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;800원을 거슬러줘야 한다면, 가장 큰 동전부터 고르는 그리디 방식은 이렇게 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;500원 선택
남은 금액 300원
100원 3개 선택
총 4개
&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;400원 + 400원 = 총 2개
&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;ldquo;큰 것부터 고르자&amp;rdquo;가 아니라,&lt;br /&gt;&lt;b&gt;그 선택이 전체적으로도 최선이 되는지&lt;/b&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;거스름돈&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;빌려줄 수 있는 학생에게 바로 빌려주기&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;차량이 나가는 지점 기준으로 카메라 설치&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;&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;/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;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;모든 경우를 확인할 수 있는가?&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;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;왜냐하면 &amp;ldquo;무엇을 먼저 선택할 것인가&amp;rdquo;를 정해야 하기 때문이다.&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;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;많은 회의를 선택하고 싶다
&amp;rarr; 회의가 빨리 끝날수록 남은 시간이 많다
&amp;rarr; 종료 시간이 빠른 회의부터 선택한다
&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;회의시작 시간종료 시간&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;A&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9&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;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;Java 코드로 쓰면 다음과 같다.&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[][] meetings = {
            {1, 4},
            {3, 5},
            {0, 6},
            {5, 7},
            {8, 9}
        };

        Arrays.sort(meetings, (a, b) -&amp;gt; Integer.compare(a[1], b[1]));

        int count = 0;
        int endTime = 0;

        for (int[] meeting : meetings) {
            int start = meeting[0];
            int end = meeting[1];

            if (start &amp;gt;= endTime) {
                count++;
                endTime = end;
            }
        }

        System.out.println(count);
    }
}
&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;Arrays.sort(meetings, (a, b) -&amp;gt; Integer.compare(a[1], b[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;h3 data-ke-size=&quot;size23&quot;&gt;1. 무조건 큰 것부터 고르는 실수&lt;/h3&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;b&gt;문제에 따라 선택 기준은 달라진다.&lt;/b&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;즉, &amp;ldquo;무엇이 가장 좋은 선택인가&amp;rdquo;를 문제에 맞게 다시 정의해야 한다.&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;p data-ke-size=&quot;size16&quot;&gt;회의실 배정 문제에서&lt;u&gt; 시작 시간 기준으로 정렬하면 틀릴 수&lt;/u&gt; 있다.&lt;br /&gt;이 문제에서는 &lt;u&gt;종료 시간 기준으로 정렬&lt;/u&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;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;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;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는 둘 다 최적의 답을 찾는 문제에서 자주 나온다.&lt;br /&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;DP&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;지금 선택이 전체 최적해로 이어지는가?&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;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;그리디 = 지금 제일 좋아 보이는 것을 고른다.
DP = 이전에 구한 답을 저장해서 다시 쓴다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리디는 선택을 반복하는 느낌이고,&lt;br /&gt;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;앞으로 그리디 문제를 풀 때는 아래 순서로 생각해보면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서확인할 것&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 46.8605%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.51163%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 40.2326%;&quot;&gt;무엇을 기준으로 선택할 것인가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.51163%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 40.2326%;&quot;&gt;정렬이 필요한가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.51163%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 40.2326%;&quot;&gt;지금 선택한 것이 나중에 손해가 되지 않는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.51163%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 40.2326%;&quot;&gt;이 선택이 항상 최적해를 만드는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.51163%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 40.2326%;&quot;&gt;반례는 없는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 class=&quot;erlang&quot;&gt;&lt;code&gt;반례가 없다면 그리디 가능성이 높다.
반례가 있다면 DP나 완전탐색을 고려해야 한다.
&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;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)이 추가&lt;/b&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) + O(N) = O(N 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;&amp;nbsp;&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;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;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;p data-ke-size=&quot;size16&quot;&gt;그래서 그리디 문제를 풀 때는 단순히 &amp;ldquo;큰 것부터 고르면 되겠다&amp;rdquo;라고 생각하면 안 된다.&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;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;&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;거스름돈 문제에서는 큰 동전이 기준이 될 수 있고,&lt;br /&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;b&gt;즉, 그리디 문제의 핵심은 &lt;span style=&quot;color: #006dd7;&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;1. 지금 무엇을 선택해야 하지?
2. 어떤 기준으로 정렬해야 하지?
3. 이 선택이 나중에도 손해가 되지 않을까?
4. 반례는 없을까?
&lt;/code&gt;&lt;/pre&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;br /&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;br /&gt;&lt;b&gt;그 선택이 왜 맞는지를 설명할 수 있는지&lt;/b&gt;&amp;nbsp;먼저 확인해야겠다.&lt;/p&gt;</description>
      <category>CS/알고리즘</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/342</guid>
      <comments>https://mymelody.tistory.com/342#entry342comment</comments>
      <pubDate>Mon, 1 Jun 2026 13:55:11 +0900</pubDate>
    </item>
    <item>
      <title>programmers) 정수 삼각형</title>
      <link>https://mymelody.tistory.com/341</link>
      <description>&lt;h1&gt;&amp;nbsp;정수 삼각형, DFS로 접근했다가 DP로 다시 풀기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 삼각형의 꼭대기에서 시작해서 아래로 내려가며 모든 경로를 탐색하면 된다고 생각했다.&lt;br /&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;각 위치까지 도달했을 때의 최대 합을 저장하면서 내려가는 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;문제 정보&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;Level 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유형&lt;/td&gt;
&lt;td&gt;DP (동적 계획법)&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;DFS / 완전탐색&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;DP&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;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;p data-ke-size=&quot;size16&quot;&gt;마지막 층까지 내려갔을 때 만들 수 있는 &lt;b&gt;최대 합&lt;/b&gt;을 구하는 문제이다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;제한사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;삼각형의 높이는 1 이상 500 이하&lt;/li&gt;
&lt;li&gt;삼각형을 이루는 숫자는 0 이상 9,999 이하의 정수&lt;/li&gt;
&lt;/ul&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;pre class=&quot;json&quot;&gt;&lt;code&gt;[
     [7],
    [3, 8],
   [8, 1, 0],
  [2, 7, 4, 4],
 [4, 5, 2, 6, 5]
]
&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;30
&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;7 &amp;rarr; 3 &amp;rarr; 8 &amp;rarr; 7 &amp;rarr; 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 경로를 선택하면 합이 30이 되어 최댓값이 된다.&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;        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5
&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;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;7
├── 3
│   ├── 8
│   └── 1
└── 8
    ├── 1
    └── 0
&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;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;그래서 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;한 층 내려갈 때마다 선택지가 두 개씩 생기기 때문에, 단순 DFS로 모든 경로를 탐색하면 경우의 수가 빠르게 늘어난다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1층: 1개
2층: 2개
3층: 4개
4층: 8개
...
&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;&amp;nbsp;&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;span&gt;각 위치에서는 아래 왼쪽 또는 아래 오른쪽으로 내려갈 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;즉, 한 층 내려갈 때마다 선택지가 2개씩 생긴다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780280983697&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1층: 1개
2층: 2개
3층: 4개
4층: 8개
5층: 16개
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;삼각형의 높이를 h라고 하면 DFS로 탐색해야 하는 경로의 수는 대략 `2^(h-1)`개가 된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;프로그래머스 정수 삼각형은 높이가 최대 500까지 가능하다. &lt;/span&gt;&lt;br /&gt;&lt;span&gt;따라서 모든 경로를 DFS로 탐색하면 최악의 경우 `2^499`개의 경로를 확인해야 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;이 정도의 경우의 수는 현실적으로 계산할 수 없기 때문에 시간초과가 난다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;처음에는 &amp;ldquo;두 갈래로 내려가니까 DFS로 풀 수 있겠다&amp;rdquo;고 생각했지만, &lt;/span&gt;&lt;br /&gt;&lt;span&gt;이 문제는 모든 경로를 직접 탐색하는 것이 아니라 각 칸까지 도달했을 때의 최대 합만 저장하면 되는 문제였다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;즉, 시간초과의 원인은 DFS 자체가 틀렸다기보다는 &lt;/span&gt;&lt;br /&gt;&lt;span&gt;이 문제에서 DFS로 모든 경로를 직접 탐색하면 경우의 수가 너무 커진다는 점을 고려하지 못한 것이다.&lt;/span&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;왜 DFS로는 어려웠는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS로도 작은 입력은 풀 수 있다.&lt;br /&gt;하지만 프로그래머스 정수 삼각형은 삼각형의 높이가 최대 500이기 때문에 모든 경로를 다 탐색하는 방식은 비효율적이다.&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;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;      3   8
       \ /
        1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1에 도착하는 방법은 두 가지다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3에서 내려오는 경우
8에서 내려오는 경우
&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;b&gt;해당 위치까지 도달했을 때 만들 수 있는 최대 합&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 1에 도착하는 모든 경로를 계속 따로 들고 갈 필요가 없다.&lt;br /&gt;1에 도착할 수 있는 최대 합만 저장하면 된다.&lt;/p&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;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;이 문제에서는 입력 배열 triangle 자체를 DP 배열처럼 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;triangle[i][j] = i층 j번째 위치까지 도착했을 때의 최대 합
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 원래 triangle[i][j]에는 현재 칸의 숫자가 들어 있었지만,&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;예를 들어 맨 위는 그대로 시작한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;triangle[0][0] = 7
&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;3까지 오는 최대 합 = 7 + 3 = 10
8까지 오는 최대 합 = 7 + 8 = 15
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 층 가운데 값 1은 위쪽 두 칸 중 더 큰 값을 선택해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;      10   15
        \ /
         1

1까지 오는 최대 합 = max(10, 15) + 1 = 16
&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;위치올 수 있는 방향&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;위쪽 두 칸 중 하나에서 올 수 있음&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;1. 맨 왼쪽&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;    7
   /
  3
&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;triangle[i][j] += triangle[i - 1][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;2. 맨 오른쪽&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;  7
   \
    8
&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;triangle[i][j] += triangle[i - 1][j - 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;3. 가운데&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;   10   15
     \ /
      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;triangle[i][j] += Math.max(triangle[i - 1][j - 1], triangle[i - 1][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;처음에는 DFS로 풀려고 해서 시간초과가 났지만, 다시 DP로 접근해서 아래와 같이 풀 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;class Solution {
    public int solution(int[][] triangle) {
        
        for (int i = 1; i &amp;lt; triangle.length; i++) {
            for (int j = 0; j &amp;lt; triangle[i].length; j++) {
                if (j == 0) {
                    triangle[i][j] += triangle[i - 1][j];
                } else if (j == triangle[i].length - 1) {
                    triangle[i][j] += triangle[i - 1][j - 1];
                } else {
                    triangle[i][j] += Math.max(triangle[i - 1][j], triangle[i - 1][j - 1]);
                }
            }
        }

        int answer = 0;

        for (int value : triangle[triangle.length - 1]) {
            answer = Math.max(value, answer);
        }

        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;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 1; i &amp;lt; triangle.length; i++)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i는 현재 층을 의미한다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int j = 0; j &amp;lt; triangle[i].length; j++)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;j는 현재 층에서의 위치를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이중 for문을 통해 삼각형의 모든 칸을 한 번씩 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 for문을 사용했기 때문에 방향이 틀린 줄 알았는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 삼각형은 오히려 for문으로 푸는 DP 문제가 맞았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 중요한 것은 for문으로 모든 경로를 직접 탐색하는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;for문으로 각 칸까지의 최대 합을 갱신해야 한다는 점&lt;/b&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;pre class=&quot;fortran&quot;&gt;&lt;code&gt;int answer = 0;

for (int value : triangle[triangle.length - 1]) {
    answer = Math.max(value, answer);
}
&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;[24, 25, 20, 27, 24]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 그중 가장 큰 값인 27이다.&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;삼각형 안의 전체 숫자 개수를 N이라고 하면 시간복잡도는 다음과 같다.&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;처음에 DFS로 모든 경로를 탐색하려고 했을 때는 경우의 수가 계속 늘어났지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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 배열을 만들지 않고, 입력으로 받은 triangle 배열 자체를 갱신한다.&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;p data-ke-size=&quot;size16&quot;&gt;단, 이 방식은 원본 triangle 배열이 변경된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트에서는 보통 문제가 되지 않지만, 원본 데이터를 보존해야 하는 상황이라면 별도의 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;구분처음 접근최종 접근&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;DFS로 모든 경로 탐색&lt;/td&gt;
&lt;td&gt;DP로 각 칸의 최대 합 저장&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;O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;위치 관리&lt;/td&gt;
&lt;td&gt;재귀에서 복잡해짐&lt;/td&gt;
&lt;td&gt;이중 for문으로 명확함&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;/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;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;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;erlang&quot;&gt;&lt;code&gt;경로를 전부 저장하거나 탐색하지 않는다.
각 칸까지의 최대 합만 저장한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 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;다시 풀 때는 아래 순서로 생각해야겠다.&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 style=&quot;width: 5.23256%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;모든 경로를 직접 탐색해야 하는 문제인지 확인한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.23256%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;같은 위치에 여러 경로로 도착할 수 있는지 확인한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.23256%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;해당 위치까지의 최댓값만 저장해도 되는지 생각한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.23256%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;dp[i][j]의 의미를 정한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.23256%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;맨 왼쪽, 맨 오른쪽, 가운데를 나누어 처리한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.23256%;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 94.6512%;&quot;&gt;마지막 줄에서 최댓값을 찾는다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;처음에는 정수 삼각형을 DFS로 풀려고 했다.&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;다시 풀면서 이 문제는 각 칸까지의 최대 합을 저장하는 DP 문제라는 것을 이해했다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;DFS처럼 모든 경로를 직접 내려가지 않는다.
DP로 각 칸까지 도달할 수 있는 최대 합을 저장한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 통해 DP에서 중요한 것은 dp[i] 또는 dp[i][j]의 의미를 먼저 정하는 것이라는 점을 다시 느꼈다.&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;triangle[i][j] = i층 j번째 위치까지 도착했을 때의 최대 합
&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;중복되는 계산이 있는지, 이전 값으로 현재 값을 만들 수 있는지부터 확인해야겠다.&lt;/p&gt;</description>
      <category>코딩테스트/프로그래머스</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/341</guid>
      <comments>https://mymelody.tistory.com/341#entry341comment</comments>
      <pubDate>Mon, 1 Jun 2026 12:41:41 +0900</pubDate>
    </item>
    <item>
      <title>DP(다이나믹 프로그래밍), 중복 계산을 줄여주는 알고리즘</title>
      <link>https://mymelody.tistory.com/340</link>
      <description>&lt;h1&gt;DP(다이나믹&amp;nbsp;프로그래밍),&amp;nbsp;중복&amp;nbsp;계산을&amp;nbsp;줄여주는&amp;nbsp;알고리즘&lt;/h1&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;처음에는 이름부터 어렵게 느껴졌다.&lt;br /&gt;Dynamic Programming이라는 이름만 보면 뭔가 복잡한 알고리즘처럼 보이지만, 핵심은 생각보다 단순하다.&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;즉, DP는 &lt;b&gt;이전에 구한 작은 문제의 답을 이용해서 더 큰 문제의 답을 구하는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. DP란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP는 &lt;b&gt;Dynamic Programming&lt;/b&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;DP의 핵심은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;핵심 개념설명&lt;/h4&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;작은 문제의 답을 먼저 구한다&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;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 DP는 &lt;b&gt;계산 결과를 적어두는 노트&lt;/b&gt;와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번 계산한 값을 노트에 적어두면, 다음에 같은 값이 필요할 때 다시 계산하지 않아도 된다.&lt;br /&gt;코딩에서는 이 노트 역할을 dp 배열이 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] dp = new int[n + 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;2. 왜 DP가 필요할까?&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칸 또는 2칸씩 올라갈 수 있다고 할 때,&lt;br /&gt;5칸 계단을 오르는 방법은 몇 가지인지 구해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 하나씩 세어볼 수도 있지만, 계단이 10칸, 100칸, 1,000칸으로 늘어나면 직접 세기 어렵다.&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;계단 문제에서는 이런 식으로 생각할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;5칸에 도착하는 방법은
4칸에서 1칸 올라오는 방법과
3칸에서 2칸 올라오는 방법을 합친 것이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 5칸의 답을 구하기 위해 4칸의 답과 3칸의 답을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dp[5] = dp[4] + dp[3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 DP는 &lt;b&gt;이전 답을 이용해서 현재 답을 구하는 방식&lt;/b&gt;이다.&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칸 또는 2칸씩 오를 수 있다고 하자.&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;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;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2칸&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3칸&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4칸&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5칸&lt;/td&gt;
&lt;td&gt;8&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;3칸 = 2칸까지 가는 방법 + 1칸까지 가는 방법
4칸 = 3칸까지 가는 방법 + 2칸까지 가는 방법
5칸 = 4칸까지 가는 방법 + 3칸까지 가는 방법
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 현재 칸까지 가는 방법은 바로 전 칸과 두 칸 전의 방법 수를 더하면 된다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;dp[i] = dp[i - 1] + dp[i - 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;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. dp 배열의 의미 정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP 문제를 풀 때 가장 먼저 해야 할 일은 dp[i]의 의미를 정하는 것이다.&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;dp[i] = 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;DP 문제는 결국 dp 배열에 무엇을 저장할 것인가를 정하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 유형dp 의미 예시&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;dp[i] = i칸까지 가는 방법 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최소 비용&lt;/td&gt;
&lt;td&gt;dp[i] = i번째까지 가는 최소 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최대 점수&lt;/td&gt;
&lt;td&gt;dp[i] = i번째까지 얻을 수 있는 최대 점수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;타일링&lt;/td&gt;
&lt;td&gt;dp[i] = i길이를 채우는 방법 수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP는 dp[i]의 의미를 잘 잡으면 절반은 해결한 것과 비슷하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 초기값 정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP에서는 모든 값을 점화식으로만 구할 수 없다.&lt;br /&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;dp[1] = 1;
dp[2] = 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1칸까지 가는 방법은 1가지이다.&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;2칸까지 가는 방법은 2가지이다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 + 1
2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 dp[1]과 dp[2]는 직접 정해준다.&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;6. 점화식 찾기&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;DP에서는 현재 값을 이전 값들로 어떻게 만들 수 있는지 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계단 문제에서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;dp[i] = dp[i - 1] + dp[i - 2]
&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;i번째 칸에 도착하는 방법은 두 가지이다.&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;i - 1칸에서 1칸 올라오기&lt;/td&gt;
&lt;td&gt;바로 전 칸에서 오는 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i - 2칸에서 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;그래서 두 경우를 더하면 i칸까지 가는 전체 방법 수가 된다.&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;스크린샷 2026-05-29 16.12.24.png&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tLlT9/dJMcadPFs15/stt2alfidKO8B4wksOcLf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tLlT9/dJMcadPFs15/stt2alfidKO8B4wksOcLf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tLlT9/dJMcadPFs15/stt2alfidKO8B4wksOcLf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtLlT9%2FdJMcadPFs15%2Fstt2alfidKO8B4wksOcLf1%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;492&quot; height=&quot;237&quot; data-filename=&quot;스크린샷 2026-05-29 16.12.24.png&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;237&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;7. Java 코드로 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계단 오르기 문제를 Java 코드로 작성하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Main {
    public static void main(String[] args) {
        int n = 5;

        int[] dp = new int[n + 1];

        dp[1] = 1;
        dp[2] = 2;

        for (int i = 3; i &amp;lt;= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }

        System.out.println(dp[n]);
    }
}
&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;8
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가장 중요한 코드는 이 부분이다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;dp[i] = dp[i - 1] + dp[i - 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;h2 data-ke-size=&quot;size26&quot;&gt;8. DP 접근 순서&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;순서질문&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;dp[i]는 무엇을 의미하는가?&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;답은 dp[n]인가, 아니면 dp 배열 중 최댓값/최솟값인가?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 특히 1번이 가장 중요하다고 느꼈다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;dp[i]의 의미를 정하지 않으면 점화식도 세우기 어렵다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP 문제를 만나면 무작정 코드를 쓰기보다, 먼저 dp[i]가 무엇을 저장하는지부터 정해야 한다.&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;9. DP와 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;DFS는 보통 모든 경우를 직접 내려가며 탐색한다.&lt;/p&gt;
&lt;pre class=&quot;diff&quot;&gt;&lt;code&gt;+를 선택하는 경우
-를 선택하는 경우
모든 가지를 끝까지 확인한다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 DP는 중복 계산을 줄이는 데 초점이 있다.&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;구분DFSDP&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;어떤 선택지가 있는가?&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;DP도 재귀로 구현할 수 있지만, 처음 공부할 때는 반복문 + dp 배열 방식이 더 이해하기 쉬운 것 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. DP는 언제 떠올리면 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 다음과 같은 느낌이 나오면 DP를 의심해볼 수 있다.&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;&quot;&gt;&lt;code&gt;현재 답을 이전 답으로 만들 수 있는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 &amp;ldquo;그렇다&amp;rdquo;라고 답할 수 있다면 DP일 가능성이 높다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. DP 문제에서 자주 나오는 유형&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;유형예시&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;최소 비용, 동전 문제&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;LCS, 편집 거리&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;&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;12. 시간복잡도와 공간복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계단 오르기 DP를 기준으로 보면, 1부터 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;O(N)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp 배열에 n개만큼 값을 저장하므로 공간복잡도도 다음과 같다.&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;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;td&gt;1부터 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;td&gt;dp 배열에 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;그 경우 공간복잡도는 O(1)로 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 처음 DP를 공부할 때는 dp 배열을 직접 만들어보는 것이 이해에 더 도움이 된다.&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;13. 내가 이해한 DP 핵심&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 DP를 공부하면서 가장 크게 이해한 점은 이것이다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;DP는 한 번 구한 답을 저장해두고 다시 사용하는 방식이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DP 문제를 풀 때 가장 먼저 해야 할 일은 dp[i]의 의미를 정하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;dp[i]는 무엇을 저장하는가?
&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;dp[i] = i칸까지 올라가는 방법의 수
&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;dp[1] = 1
dp[2] = 2
dp[i] = dp[i - 1] + dp[i - 2]
&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;pre class=&quot;erlang&quot;&gt;&lt;code&gt;한 번 구한 답을 저장해두고 다시 쓰는 것이다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트에서 DP 문제를 만나면 바로 점화식부터 찾으려고 하기보다,&lt;br /&gt;먼저 아래 순서로 생각해야겠다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;1. dp[i]의 의미를 정한다.
2. 초기값을 정한다.
3. 이전 값으로 현재 값을 만드는 규칙을 찾는다.
4. 반복문으로 dp 배열을 채운다.
&lt;/code&gt;&lt;/pre&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;스크린샷 2026-05-29 16.12.07.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJoT0R/dJMcac4kMY3/ak6Z8BNlEHomKpuNi2uEDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJoT0R/dJMcac4kMY3/ak6Z8BNlEHomKpuNi2uEDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJoT0R/dJMcac4kMY3/ak6Z8BNlEHomKpuNi2uEDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJoT0R%2FdJMcac4kMY3%2Fak6Z8BNlEHomKpuNi2uEDK%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;986&quot; height=&quot;190&quot; data-filename=&quot;스크린샷 2026-05-29 16.12.07.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/알고리즘</category>
      <author>명이나물 라이브러리</author>
      <guid isPermaLink="true">https://mymelody.tistory.com/340</guid>
      <comments>https://mymelody.tistory.com/340#entry340comment</comments>
      <pubDate>Fri, 29 May 2026 17:02:01 +0900</pubDate>
    </item>
  </channel>
</rss>