<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>놀러오세요 엘피의 숲</title>
    <link>https://elffffy.tistory.com/</link>
    <description>코린이 탈출 일지</description>
    <language>ko</language>
    <pubDate>Wed, 9 Sep 2026 05:35:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>elffffy</managingEditor>
    <image>
      <title>놀러오세요 엘피의 숲</title>
      <url>https://tistory1.daumcdn.net/tistory/8443836/attach/ce2669094ca24381b92d359fa6dae050</url>
      <link>https://elffffy.tistory.com</link>
    </image>
    <item>
      <title>[Programmers/Lv. 2/Python] 완전범죄</title>
      <link>https://elffffy.tistory.com/50</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;DP (0/1 배낭)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 완전범죄&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:122;407-528&quot;&gt;A도둑과 B도둑이 물건을 나눠 훔치는데, 물건 i를 A가 훔치면 A흔적에 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;info[i][0]&lt;/span&gt;, B가 훔치면 B흔적에 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;info[i][1]&lt;/span&gt;이 쌓인다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;21:1-21:122;407-528&quot;&gt;A흔적 합이 n 이상이거나 B흔적 합이 m 이상이면 잡힌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;26:1-26:59;631-689&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;dp[x]&lt;/span&gt;: 지금까지 처리한 물건 중 A흔적 합이 정확히 x일 때, 나머지(B흔적 합)의 최솟값.&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-29:73;690-870&quot;&gt;물건마다 두 가지 전이만 존재한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-sourcepos=&quot;28:3-29:73;734-870&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;28:3-28:66;734-797&quot;&gt;B가 훔치는 경우: A흔적(x)은 그대로, B흔적에 b를 더함&lt;/li&gt;
&lt;li data-sourcepos=&quot;29:3-29:73;800-870&quot;&gt;A가 훔치는 경우: A흔적이 a만큼 늘어난 자리로 이동, B흔적은 그대로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-sourcepos=&quot;30:1-30:84;871-954&quot;&gt;매 물건 처리 시 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;next_dp&lt;/span&gt;를 INF로 새로 초기화해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;&quot;&quot;&quot;
dp[x] : A 흔적이 x일 때 만들 수 있는 최소 B 흔적

1. 초기 상태
n = 5라면, [0, INF, INF, INF, INF]

2. 첫 번째 물건를 본다  (a, b = 2, 3)
- B가 훔치면: A 흔적 유지, B 흔적 0 + 3 = 3 =&amp;gt; dp[0] = 3
- A가 훔치면 : A 흔적 0 + 2 = 2로 이동, B 흔적은 그대로 =&amp;gt; dp[2] = 0
[3, INF, 0, INF, INF]

3. 두 번째 물건을 본다 (a, b = 1, 2)
- dp[0] = 3에서
    - B 선택 시 dp[0] = 3 + 2 = 5
    - A 선택 시 dp[1] = 3
- dp[2] = 0에서
    - B 선택 시 dp[2] = 0 + 2 = 2
    - A 선택 시 dp[3] = 0
[5, 3, 2, 0, INF]

0/1 배낭 DP의 핵심: 물건마다 A행/B행 둘 중 정확히 하나만 선택 (동시에 두 값을 더하는 게 아님)
&quot;&quot;&quot;


def solution(info, n, m):
    INF = float('inf')
    dp = [INF] * n
    dp[0] = 0

    for a, b in info:
        next_dp = [INF] * n

        for x in range(n):
            if dp[x] == INF:
                continue

            # 1. B가 훔치는 경우
            if dp[x] + b &amp;lt; m:
                next_dp[x] = min(next_dp[x], dp[x] + b)

            # 2. A가 훔치는 경우
            if x + a &amp;lt; n:
                next_dp[x + a] = min(next_dp[x + a], dp[x])

        dp = next_dp

    for x in range(n):
        if dp[x] &amp;lt; m:
            return x

    return -1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&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;O(len(info) &amp;times; n) &amp;mdash; 물건마다 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 contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;81:1-81:150;1899-2048&quot;&gt;처음엔 b가 큰 물건부터 정렬해서 B에 최대한 채우고 못 들어가면 A로 넘기는 그리디로 접근했으나, 이미 B에 배정한 물건을 나중에 재고할 수 없어 반례에서 실패했다&lt;/li&gt;
&lt;li data-sourcepos=&quot;81:1-81:150;1899-2048&quot;&gt;DP 문제인 지 몰랐는데... 내가 제일 못하는 문제였다... 어렵다... 답을 봐도 이해까지 시간이 꽤 걸린다... &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;DP 고수분들 기다리겠습니다,,,&lt;/h3&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;잘하는 법 알고싶습니다...&lt;/h3&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;736&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wPyQp/dJMcafN34Mf/xypbBYCsIbDzptugTi1qTK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wPyQp/dJMcafN34Mf/xypbBYCsIbDzptugTi1qTK/img.jpg&quot; data-alt=&quot;DP 극복해야만 해&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wPyQp/dJMcafN34Mf/xypbBYCsIbDzptugTi1qTK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwPyQp%2FdJMcafN34Mf%2FxypbBYCsIbDzptugTi1qTK%2Fimg.jpg&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;549&quot; height=&quot;439&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DP 극복해야만 해&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>  알고리즘</category>
      <category>DP</category>
      <category>배낭문제</category>
      <category>완전범죄</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/50</guid>
      <comments>https://elffffy.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 6 Jul 2026 19:20:51 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 2/Python] 바이러스 파이프</title>
      <link>https://elffffy.tistory.com/49</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 2 / &lt;span style=&quot;background-color: #ffffff; color: #7890a0; text-align: start;&quot;&gt;2025 카카오 하반기 1차&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;완전탐색, DFS/그래프 탐색, 트리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 바이러스&amp;nbsp;파이프&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:78;495-572&quot;&gt;n개의 배양체가 n-1개의 파이프로 트리 형태로 연결되어 있고, 하나의 배양체가 처음부터 감염되어 있다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;22:1-22:132;573-704&quot;&gt;파이프는 종류 단위로만 열고 닫을 수 있고, 열려 있는 동안 감염이 인접 노드로 전파된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;26:1-26:102;721-822&quot;&gt;파이프 타입별로 인접 리스트 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;arr[node][type]&lt;/span&gt;를 만들어, 특정 타입을 열었을 때 해당 타입 간선만 따라 감염이 퍼지도록 구성한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-27:131;823-953&quot;&gt;k번의 행동을 (A, B, C)의 길이 k &lt;b&gt;중복순열&lt;/b&gt;로 모두 나열하고 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;product([1,2,3], repeat=k)&lt;/span&gt;, 각 순열대로 순서대로 파이프를 열어 감염 상태를 갱신한 뒤 감염 수의 최댓값을 추적한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from itertools import product


def infect(arr, opened, start, pipe):
    if not arr[start][pipe]:
        return

    for next_n in arr[start][pipe]:
        if opened[next_n] == 1: continue
        opened[next_n] = 1
        infect(arr, opened, next_n, pipe)

    return


def solution(n, infection, edges, k):
    arr = [[[] for _ in range(4)] for _ in range(n + 1)]

    for x, y, c_type in edges:
        arr[x][c_type].append(y)
        arr[y][c_type].append(x)

    pipes_candidates = product([1, 2, 3], repeat=k)
    answer = 1

    for pipes_candidate in pipes_candidates:
        opened = [0] * (n + 1)
        opened[infection] = 1

        for pipe in pipes_candidate:
            for i in range(1, n + 1):
                if opened[i] == 1:
                    infect(arr, opened, i, pipe)

        cnt = sum(opened)
        answer = max(answer, cnt)

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 57px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 33.1395%; height: 37px;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 66.7442%; height: 37px;&quot;&gt;O(3^k &amp;middot; k &amp;middot; n) &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 후보 개수 3^k, k번 파이프 개방 &amp;times; 매번 노드 최대 n개 순회 + DFS 전파&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.1395%; height: 20px;&quot;&gt;공간 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 66.7442%; height: 20px;&quot;&gt;O(n)&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;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;88:1-88:176;2261-2436&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;itertools.permutations(iterable, r)&lt;/span&gt;은 중복 순열이 아니다! &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;&lt;b&gt;product&lt;/b&gt;&lt;/span&gt;를 써야한다!!!&amp;nbsp;&lt;/li&gt;
&lt;li data-sourcepos=&quot;90:1-90:137;2588-2724&quot;&gt;현재 내 코드는.. 매우 비효율적이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;90:1-90:137;2588-2724&quot;&gt;검색을 해보면, 비트마스크, 큐 BFS, 메모이제이션 등 다양한 해결 방법이 있어보인다..&lt;/li&gt;
&lt;li data-sourcepos=&quot;90:1-90:137;2588-2724&quot;&gt;product로 전체 후보를 만드는 것이 아니라, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;DFS/백트래킹&lt;/b&gt;&lt;/span&gt; 구조로 체크를 해야한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;90:1-90:137;2588-2724&quot;&gt;각 단계에서 갈 수 있는 방향으로 하나씩 끝까지 가보고, 돌아올 때마다 지금까지 본 것 중 최댓값을 챙겨서 위로 넘긴다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;90:1-90:137;2588-2724&quot;&gt;이렇게 한다면 시간 복잡도는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333;&quot;&gt;O(n &amp;middot; 2^k)&lt;/span&gt;&lt;/b&gt;가 된다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1783060010106&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque
from itertools import product


def infect(arr, opened, pipe, n):
    q = deque(i for i in range(1, n + 1) if opened[i])
    while q:
        cur = q.popleft()
        for next_n in arr[cur][pipe]:
            if not opened[next_n]:
                opened[next_n] = 1
                q.append(next_n)


def solution(n, infection, edges, k):
    arr = [[[] for _ in range(4)] for _ in range(n + 1)]
    for x, y, c_type in edges:
        arr[x][c_type].append(y)
        arr[y][c_type].append(x)

    def dfs(depth, last_pipe, opened):
        cnt = sum(opened)
        best = cnt                      

        if cnt == n or depth == k:
            return best

        for pipe in (1, 2, 3):
            if pipe == last_pipe:
                continue
            new_opened = opened[:]
            infect(arr, new_opened, pipe, n)
            best = max(best, dfs(depth + 1, pipe, new_opened))

        return best

    opened = [0] * (n + 1)
    opened[infection] = 1
    return dfs(0, 0, opened)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4CXiC/dJMcacKwWBm/i4DcH6D1PijiW9NtPBSErk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4CXiC/dJMcacKwWBm/i4DcH6D1PijiW9NtPBSErk/img.png&quot; data-alt=&quot;pipe=1 박스는 두 자식들 중에서 큰 값을 받아서, depth가 0인 박스로 올려보낸다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4CXiC/dJMcacKwWBm/i4DcH6D1PijiW9NtPBSErk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4CXiC%2FdJMcacKwWBm%2Fi4DcH6D1PijiW9NtPBSErk%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;730&quot; height=&quot;337&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pipe=1 박스는 두 자식들 중에서 큰 값을 받아서, depth가 0인 박스로 올려보낸다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>  알고리즘</category>
      <category>dfs</category>
      <category>그래프</category>
      <category>바이러스 파이프</category>
      <category>완전탐색</category>
      <category>중복순열</category>
      <category>트리</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/49</guid>
      <comments>https://elffffy.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 3 Jul 2026 16:12:11 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 2/Python] 지게차와 크레인</title>
      <link>https://elffffy.tistory.com/48</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 2/ &lt;span style=&quot;background-color: #ffffff; color: #7890a0; text-align: start;&quot;&gt;2025 프로그래머스 코드챌린지 1차 예선&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;BFS, 시뮬레이션, 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 지게차와&amp;nbsp;크레인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:58;419-476&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;n&amp;times;m&lt;/span&gt; 창고에 알파벳으로 구분되는 컨테이너가 놓여 있고, 요청이 들어올 때마다 특정 종류를 꺼낸다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;22:1-22:90;477-566&quot;&gt;요청 문자열 길이가 1이면 지게차 출고, 길이가 2면 크레인 출고&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;27:1-27:78;612-689&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;memo&lt;/span&gt;로 제거된 칸을 표시하고, 크레인 요청은 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;storage[y][x] == delete_w&lt;/span&gt;인 칸을 위치 상관없이 바로 제거한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;28:1-28:126;690-815&quot;&gt;지게차 요청 직전마다 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;get_border()&lt;/span&gt;로 BFS를 새로 돌려서 외부와 연결된 칸을 구한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;29:1-29:108;816-923&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;is_border(board, y, x)&lt;/span&gt;로 해당 칸이 격자 테두리 자체이거나, 이웃 중 하나라도 외부와 이어진 칸이라면 접근 가능한 것으로 판정 후 제거한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from collections import deque


def solution(storage, requests):
    n, m = len(storage), len(storage[0])
    memo = [[0] * m for _ in range(n)]

    answer = n * m
	
    # 빈 칸들 중에서 어디가 외부와 진짜로 뚫려있는지 표시하기
    def get_border():
        visited = [[0] * m for _ in range(n)]
        q = deque()

        for y in range(n):
            for x in range(m):
                if (y == 0 or x == 0 or y == n - 1 or x == m - 1) and memo[y][x] == 1:
                    visited[y][x] = 1
                    q.append((y, x))

            dy = [-1, 1, 0, 0]
            dx = [0, 0, -1, 1]

            while q:
                cy, cx = q.popleft()
                for i in range(4):
                    ny, nx = dy[i] + cy, dx[i] + cx
                    if ny &amp;lt; 0 or nx &amp;lt; 0 or ny &amp;gt;= n or nx &amp;gt;= m: continue
                    if memo[ny][nx] != 1: continue
                    if visited[ny][nx] == 1: continue

                    visited[ny][nx] = 1
                    q.append((ny, nx))

        return visited
	
    # 컨테이너가 있는 칸을 지게차로 꺼낼 수 있는지 판단하기
    def is_border(board, y, x):
        if y == 0 or x == 0 or y == n - 1 or x == m - 1:
            return True

        dy = [-1, 1, 0, 0]
        dx = [0, 0, -1, 1]

        for i in range(4):
            ny, nx = dy[i] + y, dx[i] + x
            if board[ny][nx] == 1:
                return True

        return False

    for request in requests:
        if len(request) == 2:
            delete_w = request[0]
            for y in range(n):
                for x in range(m):
                    if memo[y][x] == 1: continue
                    if storage[y][x] == delete_w:
                        memo[y][x] = 1
                        answer -= 1

        else:
            board = get_border()
            for y in range(n):
                for x in range(m):
                    if board[y][x] == 1: continue
                    if not is_border(board, y, x): continue
                    if storage[y][x] == request:
                        memo[y][x] = 1
                        answer -= 1

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 40px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O(R &amp;times; n &amp;times; m) - &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;요청 R개마다 지게차면 BFS &lt;/i&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;공간 복잡도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O(n &amp;times; m)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;121:1-121:90;3144-3233&quot;&gt;BFS에서는 큐를 활용할 것!!!&lt;/li&gt;
&lt;li data-sourcepos=&quot;122:1-122:136;3234-3369&quot;&gt;처음에는 이웃 칸이 비어있는지만 보고 접근 가능 여부를 판단했는데, 그 빈 칸 자체가 외부와 단절된 고립 공간일 수 있어 오답이었음..&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  알고리즘</category>
      <category>bfs</category>
      <category>구현</category>
      <category>시뮬레이션</category>
      <category>지게차와 크레인</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/48</guid>
      <comments>https://elffffy.tistory.com/48#entry48comment</comments>
      <pubDate>Thu, 2 Jul 2026 16:50:29 +0900</pubDate>
    </item>
    <item>
      <title>12. DI와 DIP</title>
      <link>https://elffffy.tistory.com/47</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #9d9d9d;&quot;&gt; ※ 이 글은 인프런 강의를 수강하며&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #9d9d9d;&quot;&gt;개인 학습 목적으로 이해한 내용을 정리한 글입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1️⃣ &lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;Dependency Injection(DI)&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;란&lt;/span&gt;?&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;메인 모듈이 직접 의존 객체를 생성하지 않고, 외부(injector)에서 주입받는 설계 패턴&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JIDUc/dJMcaaMzy4X/svPkdpu2Ad4rflXGfSnET0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JIDUc/dJMcaaMzy4X/svPkdpu2Ad4rflXGfSnET0/img.gif&quot; data-alt=&quot;Dependency Injection / https://miro.medium.com/1*Nd1aRAjjIfY8M0Xjb1xBVw.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JIDUc/dJMcaaMzy4X/svPkdpu2Ad4rflXGfSnET0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/JIDUc/dJMcaaMzy4X/svPkdpu2Ad4rflXGfSnET0/img.gif&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;408&quot; height=&quot;217&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dependency Injection / https://miro.medium.com/1*Nd1aRAjjIfY8M0Xjb1xBVw.gif&lt;/figcaption&gt;
&lt;/figure&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모듈 간 결합도를 낮추고&lt;/b&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;/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;background-color: #f6e199;&quot;&gt;&lt;b&gt;DI가 없는 버전&lt;/b&gt;&lt;/span&gt;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1782478195141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BackendDeveloper:
    def coding(self):
        print(&quot;백엔드 개발자 : use Java&quot;)

class FrontendDeveloper:
    def coding(self):
        print(&quot;프론트엔드 개발자: use JavaScript&quot;)

class Project:
    def __init__(self):
        self.backend = BackendDeveloper()
        self.frontend = FrontendDeveloper() 

    def implement(self):
        self.backend.coding()
        self.frontend.coding()

project = Project()
project.implement()&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;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Project&lt;/span&gt;가 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;BackendDeveloper&lt;/span&gt;와 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;FrontendDeveloper&lt;/span&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;하지만, 백엔드 개발자를 AI 개발자로 바꾸고 싶다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project 내부 코드를 직접 수정해야 하죠 &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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 이제 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;b&gt;DI를 적용한 버전&lt;/b&gt;&lt;/span&gt;을 한 번 볼까요?&lt;/p&gt;
&lt;pre id=&quot;code_1782478725419&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BackendDeveloper:
    def coding(self):
        print(&quot;백엔드 개발자 : use Java&quot;)

class FrontendDeveloper:
    def coding(self):
        print(&quot;프론트엔드 개발자 : use JavaScript&quot;)

class Project:
    def __init__(self, *developers): 
        self.developers = developers

    def implement(self):
        for dev in self.developers:
            dev.coding()

project = Project(BackendDeveloper(), FrontendDeveloper())
project.implement()&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;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Project&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;AIDeveloper()&lt;/span&gt;를 추가하더라도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스만 새로 만들면 될 뿐, 기존의 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Project&lt;/span&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;의존성을 외부에서 주입하는 것이 DI의 핵심&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;DI의 단점으로는&amp;nbsp;복잡도가 증가하고 종속성 관련 에러를 추적하기 어려울 수 있습니다.&amp;nbsp;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;2️⃣&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;Dependency Inversion Principle(DIP) &lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;란&lt;/span&gt;?&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt; &amp;zwj;♀️ &quot;그런데 잠깐,&amp;nbsp; &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Project&lt;/span&gt;가 여전히 하위 클래스에 의존하고 있는거 아닌가요?&quot;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&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;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;coding&lt;/span&gt; 메서드가 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;deleting&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; &lt;/span&gt;메서드로 바뀐다면, 런타임에서 에러가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 설계 단계에서 막아주는 원칙이 바로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;DIP&lt;/b&gt;&lt;/span&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상위 모듈은 하위 모듈에 의존해서는 안 된다. 둘 다 &lt;b&gt;추상화&lt;/b&gt;에 의존해야 한다.&lt;/li&gt;
&lt;li&gt;추상화는 세부 사항에 의존해서는 안 된다. 세부 사항이 추상화를 따라야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &quot;구체적인 클래스 말고, &lt;b&gt;인터페이스만 바라봐&lt;/b&gt;&quot;라는 뜻인데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DIP가 적용된 버전을 보겠습니다!&lt;/p&gt;
&lt;pre id=&quot;code_1782479926131&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from abc import ABC, abstractmethod

class Developer(ABC):      
    @abstractmethod
    def coding(self):
        pass

class BackendDeveloper(Developer):
    def coding(self):
        print(&quot;백엔드 개발자 : use Java&quot;)

class FrontendDeveloper(Developer):
    def coding(self):
        print(&quot;프론트엔드 개발자 : use JavaScript&quot;)

class Project:
    def __init__(self, *developers: Developer):
        self.developers = developers

    def implement(self):
        for dev in self.developers:
            dev.coding()

project = Project(BackendDeveloper(), FrontendDeveloper())
project.implement()&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;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Developer&lt;/span&gt;를 상속받지 않은 클래스는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;coding()&lt;/span&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;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Project&lt;/span&gt;는 구체적인 클래스가 무엇인지 전혀 몰라도 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;Developer&lt;/span&gt;라는 약속만 지키면 누구든 주입할 수 있게 됩니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-end=&quot;2140&quot; data-start=&quot;2109&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  마무리&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘도 다소 어려운 개념을 공부한 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드와 함께 보니 확실히 이해는 좀 더 빠르게 된 것 같지만,,,&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프로젝트 클래스 설계할 일이 있다면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;DI 구조와 DIP 원칙을 떠올릴 수 있으면 좋을 것 같다!&lt;/span&gt;&lt;/p&gt;</description>
      <category>  CS</category>
      <category>Dependency Injection</category>
      <category>Di</category>
      <category>dip</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/47</guid>
      <comments>https://elffffy.tistory.com/47#entry47comment</comments>
      <pubDate>Fri, 26 Jun 2026 22:23:30 +0900</pubDate>
    </item>
    <item>
      <title>[토이 PJT #1] VLM이란? - 이미지를 이해하는 AI 만들기</title>
      <link>https://elffffy.tistory.com/46</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;발표를 들을 때,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;말로만 설명하는 것보다&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;말과 시각자료가 함께할 때 더 오래 기억에 남지 않으신가요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R9h15/dJMcahdS4lP/eLvLFvekIXwgK3aigCxcZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R9h15/dJMcahdS4lP/eLvLFvekIXwgK3aigCxcZK/img.jpg&quot; data-alt=&quot;발표에 ppt 빠지면 섭섭하지~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R9h15/dJMcahdS4lP/eLvLFvekIXwgK3aigCxcZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR9h15%2FdJMcahdS4lP%2FeLvLFvekIXwgK3aigCxcZK%2Fimg.jpg&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;325&quot; height=&quot;271&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;발표에 ppt 빠지면 섭섭하지~&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 AI에게 질문할 때도&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이미지와 함께 물어본다면 어떨까요?&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;지난 글에서 소개한 영화 챗봇 프로젝트, 기억하시나요?&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;(보고 오시는 걸 추천드립니다 )&lt;/p&gt;
&lt;figure id=&quot;og_1782473434248&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[토이 PJT #1] RAG란? - 검색해서 답하는 AI 만들기&quot; data-og-description=&quot;&amp;quot;몸이 아프다&amp;quot; 하면 병원을 가죠.기침이랑 열이 난다 &amp;rarr; 내과귀가 좀 아프다 &amp;rarr; 이비인후과충치 때문에 아프다 &amp;rarr; 치과넘어져서 골절됐다 &amp;rarr; 정형외과증상에 따라 우리는 다른 원을 찾아갑니다.&quot; data-og-host=&quot;elffffy.tistory.com&quot; data-og-source-url=&quot;https://elffffy.tistory.com/44&quot; data-og-url=&quot;https://elffffy.tistory.com/44&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bq0czD/dJMb8VNF0jd/YD74Clv6J4B4CG2T1BS6K0/img.jpg?width=735&amp;amp;height=751&amp;amp;face=0_0_735_751,https://scrap.kakaocdn.net/dn/oXR1h/dJMb8Z3BLVf/S7Vc71CbNtgWcAXsdJ2qyK/img.jpg?width=735&amp;amp;height=751&amp;amp;face=0_0_735_751,https://scrap.kakaocdn.net/dn/d9MEl0/dJMb8Rkcoup/i6XkZe8rfVRSIkyTFr6oz0/img.jpg?width=930&amp;amp;height=930&amp;amp;face=0_0_930_930&quot;&gt;&lt;a href=&quot;https://elffffy.tistory.com/44&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://elffffy.tistory.com/44&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bq0czD/dJMb8VNF0jd/YD74Clv6J4B4CG2T1BS6K0/img.jpg?width=735&amp;amp;height=751&amp;amp;face=0_0_735_751,https://scrap.kakaocdn.net/dn/oXR1h/dJMb8Z3BLVf/S7Vc71CbNtgWcAXsdJ2qyK/img.jpg?width=735&amp;amp;height=751&amp;amp;face=0_0_735_751,https://scrap.kakaocdn.net/dn/d9MEl0/dJMb8Rkcoup/i6XkZe8rfVRSIkyTFr6oz0/img.jpg?width=930&amp;amp;height=930&amp;amp;face=0_0_930_930');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[토이 PJT #1] RAG란? - 검색해서 답하는 AI 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&quot;몸이 아프다&quot; 하면 병원을 가죠.기침이랑 열이 난다 &amp;rarr; 내과귀가 좀 아프다 &amp;rarr; 이비인후과충치 때문에 아프다 &amp;rarr; 치과넘어져서 골절됐다 &amp;rarr; 정형외과증상에 따라 우리는 다른 원을 찾아갑니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;elffffy.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이 챗봇은 질문의 유형에 따라 네 가지 방식으로 처리합니다.&lt;br /&gt;그 중 &lt;b&gt;이미지가 첨부된 질문&lt;/b&gt;은 &lt;b&gt;VLM 경로&lt;/b&gt;로 전달되는데요!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;VLM&lt;/b&gt;&lt;/span&gt;이 무엇인지,&lt;br /&gt;그리고 어떤 흐름으로 동작하는지 함께 살펴보겠습니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1️⃣ VLM이 뭔가요?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Vision Language Model의 줄임말로, 이미지와 언어를 함께 이해하는 AI 모델&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LLM은 텍스트만 이해합니다.&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;p data-ke-size=&quot;size16&quot;&gt;  &quot;이 포스터 무슨 영화야?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 이미지를 바탕으로 한 질문에는 답하지 못합니다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 시각 언어 모델인 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;VLM&lt;/b&gt;&lt;/span&gt;은 가능한데요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 세 가지 단계로 동작합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;이미지 인코딩&lt;/b&gt; : 이미지를 숫자 벡터로 변환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;입력 결합&lt;/b&gt; : 텍스트 질문과 이미지 벡터를 함께 모델에 입력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;답변 생성&lt;/b&gt; : 텍스트와 이미지 맥락을 동시에 고려해서 답변을 생성합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 단계를 통해 AI는 이미지를 이해하게 됩니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼, 이제부터!&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구체적인&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;VLM의 흐름&lt;/span&gt;&lt;/b&gt;에 대해 한 번 살펴볼까요?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2️⃣ VLM &lt;/b&gt;&lt;b&gt;흐름 살펴보기&lt;/b&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 1단계 : 이미지 인코딩&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLM 모델이 이미지를 이해할 수 있도록 &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; &amp;zwj;♀️ &quot;파일 경로를 던져주면 안되나요?&quot;&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;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;p data-ke-size=&quot;size16&quot;&gt;서버가 직접 파일을 읽어야 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL을 넘기면 외부 접근이 필요하게 됩니다.&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;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;Base64 인코딩 방식&lt;/b&gt;&lt;/span&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 바이너리 데이터를 텍스트 문자열로 변환해서 그대로 API에 실어 보내는 방식입니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 2단계 : 멀티모달 메시지 구성&lt;/h4&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;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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 텍스트와 이미지 사이의 관계를 파악할 수 없게 됩니다 &lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 3단계 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성&lt;/h4&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;일반 LLM과 달리,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLM은 이미지 속 색감, 구도, 텍스트까지 함께 읽고,&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 data-ke-size=&quot;size16&quot;&gt;그래서 영화 포스터를 함께 첨부한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장르, 분위기, 등장인물까지 추론해서 답을 해줄 수 있는 것이죠!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 읽는 AI가 아닌, 읽고 보는 AI.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;VLM&lt;/b&gt;&lt;/span&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;다음 글에서는 RAG와 VLM을 활용한 챗봇에 대해 살펴보도록 하겠습니다!&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;</description>
      <category>  AI</category>
      <category>base64 인코딩</category>
      <category>VLM</category>
      <category>이미지 인코딩</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/46</guid>
      <comments>https://elffffy.tistory.com/46#entry46comment</comments>
      <pubDate>Fri, 26 Jun 2026 20:56:57 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 2/Python] 비밀 코드 해독</title>
      <link>https://elffffy.tistory.com/45</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 2 / &lt;span style=&quot;background-color: #ffffff; color: #7890a0; text-align: start;&quot;&gt;2025 프로그래머스 코드챌린지 1차 예선&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;완전탐색, 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 비밀&amp;nbsp;코드&amp;nbsp;해독&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:62;349-410&quot;&gt;1~n 사이 숫자 중 5개를 고른 비밀코드가 있고, q번의 힌트가 주어진다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;22:1-22:40;411-450&quot;&gt;모든 힌트 조건을 동시에 만족하는 5개 조합의 경우의 수를 구한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;26:1-26:43;467-509&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;combinations(1..n, 5)&lt;/span&gt;로 후보 조합을 전부 생성한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-27:58;510-567&quot;&gt;각 후보에 대해 모든 쿼리를 순회하고 모든 쿼리를 통과한 후보만 카운트 한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;29:1-29:67;592-658&quot;&gt;내부 교집합 계산은 set 연산 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;case_set &amp;amp; query_set&lt;/span&gt;을 쓰면 정렬 순서에 무관하게 정확하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from itertools import combinations


def solution(n, q, ans):
    nums = [i for i in range(1, n + 1)]
    possible_cases = list(combinations(nums, 5))
    answer = 0

    for case in possible_cases:
        check = 0
        for i in range(len(q)):
            cnt = 0
            case_set = set(case)
            query_set = set(q[i])

            cnt += len(case_set &amp;amp; query_set)

            if cnt == ans[i]:
                check += 1

        if check == len(q):
            answer += 1

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&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;O(C(n,5) &amp;times; len(q))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공간 복잡도&lt;/td&gt;
&lt;td&gt;O(C(n,5))&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;80:1-80:143;1573-1715&quot;&gt;내부 이중 루프에서 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;if x &amp;lt; y: break&lt;/span&gt;는 배열이 정렬되어 있다고 암묵적으로 가정해야한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;80:1-80:143;1573-1715&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt; &amp;amp;&lt;/span&gt;&lt;/b&gt; 연산자로 교집합을 구하려면 &lt;b&gt;양쪽 모두 set&lt;/b&gt;이어야 한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;80:1-80:143;1573-1715&quot;&gt;list/tuple이 섞이면 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;.intersection()&lt;/span&gt;&lt;/b&gt;을 쓰자!!!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  알고리즘</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/45</guid>
      <comments>https://elffffy.tistory.com/45#entry45comment</comments>
      <pubDate>Fri, 26 Jun 2026 17:17:12 +0900</pubDate>
    </item>
    <item>
      <title>[토이 PJT #1] RAG란? - 검색해서 답하는 AI 만들기</title>
      <link>https://elffffy.tistory.com/44</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;487&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKA1GE/dJMcaglF30i/iJdZ3Dx3XFfRkGBtF8L3WK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKA1GE/dJMcaglF30i/iJdZ3Dx3XFfRkGBtF8L3WK/img.jpg&quot; data-alt=&quot;아플 땐 병원을 가시오.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKA1GE/dJMcaglF30i/iJdZ3Dx3XFfRkGBtF8L3WK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKA1GE%2FdJMcaglF30i%2FiJdZ3Dx3XFfRkGBtF8L3WK%2Fimg.jpg&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;407&quot; height=&quot;383&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;487&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아플 땐 병원을 가시오.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;&quot;몸이 아프다&quot; &lt;/span&gt;&lt;span data-newtext-seq=&quot;9&quot;&gt;하면 병원을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;16&quot;&gt;가죠.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;기침이랑 열이 &lt;/span&gt;&lt;span data-newtext-seq=&quot;8&quot;&gt;난다 &amp;rarr; 내과&lt;/span&gt;&lt;br /&gt;&lt;span data-newtext-seq=&quot;8&quot;&gt;귀가 &lt;/span&gt;&lt;span data-newtext-seq=&quot;19&quot;&gt;좀 아프다 &amp;rarr; &lt;/span&gt;&lt;span data-newtext-seq=&quot;27&quot;&gt;이비인후과&lt;/span&gt;&lt;br /&gt;&lt;span data-newtext-seq=&quot;27&quot;&gt;충치 때문에 &lt;/span&gt;&lt;span data-newtext-seq=&quot;40&quot;&gt;아프다 &amp;rarr; 치과&lt;/span&gt;&lt;br /&gt;&lt;span data-newtext-seq=&quot;40&quot;&gt;넘어져서&lt;/span&gt;&lt;span data-newtext-seq=&quot;53&quot;&gt; 골절됐다 &amp;rarr; &lt;/span&gt;&lt;span data-newtext-seq=&quot;61&quot;&gt;정형외과&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;증상에 따라 &lt;/span&gt;&lt;span data-newtext-seq=&quot;7&quot;&gt;우리는 다른 원을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;18&quot;&gt;찾아갑니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;이번에 &lt;/span&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;만들어본 토이 프로젝트도 &lt;/span&gt;&lt;span data-newtext-seq=&quot;18&quot;&gt;똑같은 구조입니다!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;18&quot;&gt;영화를 &lt;/span&gt;&lt;span data-newtext-seq=&quot;33&quot;&gt;주제로 질문이 들어오면, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;47&quot;&gt;AI가 자동으로 &lt;b&gt;질문 &lt;/b&gt;&lt;/span&gt;&lt;span data-newtext-seq=&quot;59&quot;&gt;&lt;b&gt;유형을 판단&lt;/b&gt;해서 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;59&quot;&gt;&lt;b&gt;네 가지 &lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span data-newtext-seq=&quot;73&quot;&gt;처리 방식 중 하나로 &lt;/span&gt;&lt;/b&gt;&lt;span data-newtext-seq=&quot;85&quot;&gt;&lt;b&gt;보내주는 구조&lt;/b&gt;예요.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;그 &lt;/span&gt;&lt;span data-newtext-seq=&quot;2&quot;&gt;네 가지 중 오늘 &lt;/span&gt;&lt;span data-newtext-seq=&quot;12&quot;&gt;집중적으로 다룰 건 &lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;RAG&lt;/b&gt;&lt;/span&gt;&lt;span data-newtext-seq=&quot;25&quot;&gt;입니다!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;68&quot;&gt;지금부터 &lt;/span&gt;&lt;span data-newtext-seq=&quot;79&quot;&gt;알아보겠습니다!&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1️⃣ RAG가 뭔가요?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Retrieval-Augmented Generation의 줄임말로, &lt;br /&gt;관련 문서를 먼저 검색해온 뒤, 그 내용을 바탕으로 AI가 답변을 생성하는 방식&lt;/blockquote&gt;
&lt;/div&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;zwj;♂️ 그럼, C&lt;i&gt;hatGPT나 Cluade랑 똑같은 거 아닌가?&lt;/i&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 LLM은 학습 데이터를 기반으로 답변을 하기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 데이터에 없는 정보는 그럴 듯하게 지어내서 답을 제공해주는 경우도 있습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG는 이 할루시네이션 문제를 이렇게 해결합니다!&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;➡️ 답하기 전에 먼저 관련 문서를 찾고, 그 문서를 참고해서 답하자.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마나 똑똑하게요,,, 이 녀석은 헛소리 하지 않는다는 뜻이죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Uh1B/dJMcaff4ydj/n83z66Lug6N4rkCJjelYP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Uh1B/dJMcaff4ydj/n83z66Lug6N4rkCJjelYP1/img.jpg&quot; data-alt=&quot;??: 헛소리 하지마 임마 / RAG:네, 형.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Uh1B/dJMcaff4ydj/n83z66Lug6N4rkCJjelYP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Uh1B%2FdJMcaff4ydj%2Fn83z66Lug6N4rkCJjelYP1%2Fimg.jpg&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;480&quot; height=&quot;360&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;??: 헛소리 하지마 임마 / RAG:네, 형.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 두 단계로 나뉩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Retrieval&lt;/b&gt; (검색) : 사용자 질문과 관련된 문서 조각을 벡터 DB에서 꺼내온다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Generation&lt;/b&gt; (생성) : 꺼내온 문서를 context로 넣어서 LLM이 답변을 만든다.&lt;/li&gt;
&lt;/ol&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;RAG의 흐름&lt;/span&gt;&lt;/b&gt;에 대해 한 번 살펴볼까요?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ &lt;b&gt;RAG 흐름 살펴보기&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✨ 1단계 : 문서 준비 &amp;middot; 청킹&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AI가 참고할 문서를 준비하는 단계입니다!&lt;/span&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;PDF, 웹페이지, DB 등 다양한 소스에서 텍스트를 가져온 뒤, 이를 적당한 크기로 잘라냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;청킹(Chunking)&lt;/b&gt;&lt;/span&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;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;blockquote data-ke-style=&quot;style2&quot;&gt;  실무에서는 보통&lt;b&gt;100~500 토큰 &lt;/b&gt;단위로 자르고, 청크 간에 약간 &lt;b&gt;겹치게 설정&lt;/b&gt;해서 문장이 끊기는 걸 방지하기도 해요!&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 2단계 :&lt;span&gt; 임베딩 &amp;middot; 벡터 저장&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘라낸 텍스트 조각들을 숫자 벡터로 변환해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;임베딩(Embedding)&lt;/b&gt;&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;변환된 벡터는 &lt;b&gt;Vector DB&lt;/b&gt;에 저장해두고, 나중에 빠르게 검색할 수 있도록 준비합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbX42p/dJMcabLxKbB/CUDtyvggUZPd7tNdYhYiNk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbX42p/dJMcabLxKbB/CUDtyvggUZPd7tNdYhYiNk/img.jpg&quot; data-alt=&quot;Vector DB/https://weaviate.io/assets/images/vector-search-c9852b39f62abb6122b2123e6d5f7ed5.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbX42p/dJMcabLxKbB/CUDtyvggUZPd7tNdYhYiNk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbX42p%2FdJMcabLxKbB%2FCUDtyvggUZPd7tNdYhYiNk%2Fimg.jpg&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;551&quot; height=&quot;563&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vector DB/https://weaviate.io/assets/images/vector-search-c9852b39f62abb6122b2123e6d5f7ed5.jpg&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 3단계 :&lt;span&gt;&lt;span&gt; 검색&lt;/span&gt;&lt;/span&gt;&lt;/h4&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;p data-ke-size=&quot;size16&quot;&gt;그리고 Vector DB에서 이 벡터와 가장 유사한 문서 조각들을 찾아냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정이 바로 RAG의 &quot;R&quot;을 담당하고 있는 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;Retrieval&lt;/b&gt;&lt;/span&gt; 검색입니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;✨ 4단계 :&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색된 문서 조각들을 사용자의 질문과 함께 LLM에게 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 이 자료를 바탕으로 답변을 생성하게 됩니다!&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;이 과정이 바로 RAG의 &quot;G&quot;를 담당하고 있는 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;b&gt;Generation&lt;/b&gt;&lt;/span&gt; 생성입니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇이든 아는 척 답하는 AI가 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 문서를 찾아보고 그 범위 안에서만 답하게 만드는 것!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것이 바로 RAG의 핵심입니다.&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;다음 글에서는 텍스트가 아닌 이미지를 이해하는 AI, &lt;b&gt;VLM&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;/p&gt;</description>
      <category>  AI</category>
      <category>GENERATION</category>
      <category>llm</category>
      <category>RAG</category>
      <category>Retrieval</category>
      <category>vector db</category>
      <category>임베팅</category>
      <category>청킹</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/44</guid>
      <comments>https://elffffy.tistory.com/44#entry44comment</comments>
      <pubDate>Thu, 25 Jun 2026 22:35:18 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 2/Python] 서버 증설 횟수</title>
      <link>https://elffffy.tistory.com/43</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 80px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 2 / &lt;span style=&quot;background-color: #ffffff; color: #7890a0; text-align: start;&quot;&gt;2025 프로그래머스 코드챌린지 2차 예선&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;그리디, 시뮬레이션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 서버 증설 횟수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:87;345-431&quot;&gt;0~23시 24개 시간대에 시간당 접속자 수 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;players[t]&lt;/span&gt;가 주어진다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;21:1-21:87;345-431&quot;&gt;서버 1대는 m명을 처리하며 증설 후 k시간 뒤 자동 반납된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;26:1-26:121;495-615&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;server_time_limit[t + k] = new_server_cnt&lt;/span&gt;로 만료 시각을 키, 반납 대수를 값으로 딕셔너리에 기록해두고, 매 시간대 시작 시 해당 키가 있으면 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;server_cnt&lt;/span&gt;에서 뺀다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-27:128;616-743&quot;&gt;현재 시각 t에서 필요한 총 서버 수는 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;players[t] // m&lt;/span&gt;이고, 이미 server_cnt대가 돌고 있으므로 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;new_server_cnt = players[t] // m - server_cnt&lt;/span&gt;만큼만 증설한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from collections import defaultdict


def solution(players, m, k):
    answer = 0
    server_cnt = 0
    server_time_limit = defaultdict()

    for t in range(24):
        if t in server_time_limit.keys():
            server_cnt -= server_time_limit[t]
        if players[t] &amp;lt; m or players[t] &amp;lt; m * server_cnt + 1:
            continue

        new_server_cnt = players[t] // m - server_cnt
        if new_server_cnt:
            server_cnt += new_server_cnt
            answer += new_server_cnt
            server_time_limit[t + k] = new_server_cnt

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&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;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공간 복잡도&lt;/td&gt;
&lt;td&gt;O(k)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;72:1-72:115;1669-1783&quot;&gt;처음에 현재 가동 중인 서버 수를 빼지 않았었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;이미&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;server_cnt&lt;/span&gt;대가 돌고 있다는 점을 유의하자!!&lt;/li&gt;
&lt;li data-sourcepos=&quot;73:1-73:122;1784-1905&quot;&gt;반납 시간을 기록하기 위해서 처음에는 리스트로 관리하고자 했으나, 만료시각과 반납 대수를 함께 정리해야 했으므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;딕셔너리&lt;/b&gt;를 활용했다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1782371418352&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Programmers/Lv. 1/Python] 노란불 신호등&quot; data-og-description=&quot;  문제 정보난이도Lv. 1 / 2025 카카오 하반기 1차유형수학, 브루트포스, LCM언어Python플랫폼 / 제목Programmers / 노란불 신호등   문제 분석n개의 신호등이 각각 초록 &amp;rarr; 노란 &amp;rarr; 빨간 순서로 반복되&quot; data-og-host=&quot;elffffy.tistory.com&quot; data-og-source-url=&quot;https://elffffy.tistory.com/42&quot; data-og-url=&quot;https://elffffy.tistory.com/42&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bjdKgn/dJMb83Ss7m3/1yFOqwtR57Nujd2xg7FMKK/img.jpg?width=736&amp;amp;height=552&amp;amp;face=315_227_446_358,https://scrap.kakaocdn.net/dn/dIjy7n/dJMb8WeJVz3/e2DyZclxRE81izETqdlhT0/img.jpg?width=736&amp;amp;height=552&amp;amp;face=315_227_446_358,https://scrap.kakaocdn.net/dn/b4MpbO/dJMb9aKO6cq/HQwZgFeyMUcownkLG7iNgK/img.jpg?width=930&amp;amp;height=930&amp;amp;face=0_0_930_930&quot;&gt;&lt;a href=&quot;https://elffffy.tistory.com/42&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://elffffy.tistory.com/42&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bjdKgn/dJMb83Ss7m3/1yFOqwtR57Nujd2xg7FMKK/img.jpg?width=736&amp;amp;height=552&amp;amp;face=315_227_446_358,https://scrap.kakaocdn.net/dn/dIjy7n/dJMb8WeJVz3/e2DyZclxRE81izETqdlhT0/img.jpg?width=736&amp;amp;height=552&amp;amp;face=315_227_446_358,https://scrap.kakaocdn.net/dn/b4MpbO/dJMb9aKO6cq/HQwZgFeyMUcownkLG7iNgK/img.jpg?width=930&amp;amp;height=930&amp;amp;face=0_0_930_930');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Programmers/Lv. 1/Python] 노란불 신호등&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  문제 정보난이도Lv. 1 / 2025 카카오 하반기 1차유형수학, 브루트포스, LCM언어Python플랫폼 / 제목Programmers / 노란불 신호등   문제 분석n개의 신호등이 각각 초록 &amp;rarr; 노란 &amp;rarr; 빨간 순서로 반복되&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;elffffy.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;어제 풀었던 1단계 노란불 신호등보다 2단계 서버 증설 횟수가 더 쉬운 느낌이랄까..?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;물론, 문제를 읽고 고민하는 시간은 있었지만,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;난이도 설정이 조금 잘못되어 있는 것 같다..ㅇㅇ..&lt;/span&gt;&lt;/p&gt;</description>
      <category>  알고리즘</category>
      <category>그리디</category>
      <category>서버 증설 횟수</category>
      <category>시뮬레이션</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/43</guid>
      <comments>https://elffffy.tistory.com/43#entry43comment</comments>
      <pubDate>Thu, 25 Jun 2026 16:12:27 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 1/Python] 노란불 신호등</title>
      <link>https://elffffy.tistory.com/42</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chhA11/dJMcabkqWz0/YHv7GFpIdzLMKnkl0Ftzk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chhA11/dJMcabkqWz0/YHv7GFpIdzLMKnkl0Ftzk1/img.jpg&quot; data-alt=&quot;레벨 1일리가 없어.. 없다고!!!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chhA11/dJMcabkqWz0/YHv7GFpIdzLMKnkl0Ftzk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchhA11%2FdJMcabkqWz0%2FYHv7GFpIdzLMKnkl0Ftzk1%2Fimg.jpg&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;560&quot; height=&quot;420&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레벨 1일리가 없어.. 없다고!!!!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 1 / 2025 카카오 하반기 1차&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;수학, 브루트포스, LCM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 노란불&amp;nbsp;신호등&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;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:71;310-380&quot;&gt;n개의 신호등이 각각 초록 &amp;rarr; 노란 &amp;rarr; 빨간 순서로 반복되며, 모든 신호등이 동시에 노란불이 되는 가장 빠른 시각을 구한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;22:1-22:50;381-430&quot;&gt;신호등마다 주기가 다르고, 동시에 노란불인 경우가 없으면 -1을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;26:1-26:64;447-510&quot;&gt;각 신호등의 주기를 구하고, 전체 주기의 LCM을 탐색 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;limit&lt;/span&gt;&lt;/span&gt;으로 설정한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-27:94;511-604&quot;&gt;매 시각 time마다 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;yellow_start = time % period&lt;/span&gt;를 계산해, &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;G &amp;lt; yellow_start &amp;lt;= G+Y&lt;/span&gt; 범위이면 노란불로 판별한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def minimal_common(a, b):
    for i in range(max(a, b), (a * b) + 1):
        if i % a == 0 and i % b == 0:
            return i


def solution(signals):
    plus = [sum(signal) for signal in signals]

    limit = plus[0]
    for p in plus[1:]:
        limit = minimal_common(limit, p)

    check = 0
    for time in range(2, limit + 1):
        for i in range(len(signals)):
            signal = signals[i]
            yellow_start = time % plus[i]
            if signal[0] &amp;lt; yellow_start &amp;lt;= signal[0] + signal[1]:
                check += 1
            else:
                break

        if check == len(signals):
            return time
        else:
            check = 0

    return -1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&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;O(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 contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이게 레벨 1..? 절대 그럴 일 없다.. 없어...&lt;/li&gt;
&lt;li data-sourcepos=&quot;81:1-81:120;1747-1866&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;minimal_common&lt;/span&gt;대신 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;math.lcm()&lt;/span&gt;&lt;/b&gt;을 활용할 수 있다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;81:1-81:120;1747-1866&quot;&gt;limit을 무엇으로 할까에 대해 고민을 많이 했었다. 모든 신호등의 초+노+빨 불의 합을 구하고, 그 값들의 최소공배수가 limit이 되는 걸 명심하자!!!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>  알고리즘</category>
      <category>lcm</category>
      <category>노란불신호등</category>
      <category>브루트포스</category>
      <category>수학</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/42</guid>
      <comments>https://elffffy.tistory.com/42#entry42comment</comments>
      <pubDate>Wed, 24 Jun 2026 21:37:44 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers/Lv. 3/Python] 파괴되지 않은 건물</title>
      <link>https://elffffy.tistory.com/41</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 106px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Lv. 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;유형&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;2차원 누적합, 구간 업데이트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;플랫폼 / 제목&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Programmers / 파괴되지&amp;nbsp;않은&amp;nbsp;건물&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 분석&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;21:1-21:68;377-444&quot;&gt;board에 건물 내구도가 주어지고, 여러 스킬이 각각 사각형 범위에 일정 수치만큼 내구도를 증감시킨다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;22:1-22:52;445-496&quot;&gt;모든 스킬을 적용한 뒤, 내구도가 0보다 큰 건물의 개수를 구해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  풀이 아이디어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;27:1-28:121;575-766&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt; (n+1) &amp;times; (m+1)&lt;/span&gt; 크기의 memo 배열을 두고, 스킬마다 &lt;b&gt;사각형의 네 모서리에만 부호를 맞춰 표시&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;29:1-29:72;767-838&quot;&gt;이후 memo를 &lt;b&gt;가로 방향 누적합 &amp;rarr; 세로 방향 누적합&lt;/b&gt; 순으로 두 번 훑으면, 각 칸에 실제로 적용된 변화량이 복원된다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;30:1-30:51;839-889&quot;&gt;마지막으로 &lt;span style=&quot;background-color: #dddddd; color: #ef5369;&quot;&gt;board[y][x] + memo[y][x] &amp;gt; 0&lt;/span&gt;인 칸의 개수를 센다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def solution(board, skill):
    n, m = len(board), len(board[0])
    memo = [[0] * (m + 1) for _ in range(n + 1)]

    for s_type, y1, x1, y2, x2, degree in skill:
        if s_type == 1:
            memo[y1][x1] -= degree
            memo[y1][x2 + 1] += degree
            memo[y2 + 1][x1] += degree
            memo[y2 + 1][x2 + 1] -= degree
        else:
            memo[y1][x1] += degree
            memo[y1][x2 + 1] -= degree
            memo[y2 + 1][x1] -= degree
            memo[y2 + 1][x2 + 1] += degree

    for y in range(n + 1):
        for x in range(m + 1):
            if x == 0: continue
            memo[y][x] += memo[y][x - 1]

    for x in range(m + 1):
        for y in range(n + 1):
            if y == 0: continue
            memo[y][x] += memo[y - 1][x]

    answer = 0
    for by in range(n):
        for bx in range(m):
            final_v = board[by][bx] + memo[by][bx]
            if final_v &amp;gt; 0:
                answer += 1

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⏱️ 시간/공간 복잡도&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 40px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;시간 복잡도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O(s + n&amp;times;m)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;공간 복잡도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O(n&amp;times;m)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  배운 점 / 실수했던 부분&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkG0lG/dJMcagF0kBK/odtiHlaRV9zT4hvePthZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkG0lG/dJMcagF0kBK/odtiHlaRV9zT4hvePthZB1/img.png&quot; data-alt=&quot;어머, 이게 뭐야!!!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkG0lG/dJMcagF0kBK/odtiHlaRV9zT4hvePthZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkG0lG%2FdJMcagF0kBK%2FodtiHlaRV9zT4hvePthZB1%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;673&quot; height=&quot;492&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;어머, 이게 뭐야!!!!&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 코드는 스킬마다 사각형 범위를 &lt;b&gt;직접 이중 for문으로 순회&lt;/b&gt;하며 memo에 누적했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;값을 바로 board에 더하지 않고 memo에 모아뒀다가 나중에 더하기 &lt;/i&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;기록 자체를 O(1)&lt;/b&gt;로 만드는 것이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 사각형에서는 &lt;b&gt;네 모서리에 부호를 맞춰 표시하고 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가로&amp;middot;세로 누적합을 각각 한 번씩 적용하는 방식&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;/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;</description>
      <category>  알고리즘</category>
      <category>2차원 배열</category>
      <category>구간 업데이트</category>
      <category>누적합</category>
      <category>파괴되지 않은 건물</category>
      <author>elffffy</author>
      <guid isPermaLink="true">https://elffffy.tistory.com/41</guid>
      <comments>https://elffffy.tistory.com/41#entry41comment</comments>
      <pubDate>Sun, 21 Jun 2026 18:17:22 +0900</pubDate>
    </item>
  </channel>
</rss>