<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>신이 나는 프로그래밍</title>
    <link>https://junworld.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 6 Jul 2026 05:44:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>junl</managingEditor>
    <item>
      <title>sql lv3) 헤비 유저가 소유한 장소</title>
      <link>https://junworld.tistory.com/2121</link>
      <description>&lt;pre id=&quot;code_1783260063762&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 코드를 입력하세요
# 서브쿼리 복수 리턴은 = 이 아닌 IN 으로 써야함.
# 갯수 조건은 GROUBY HAVING COUNT(TAR) &amp;gt;= 2 처럼 작성
# WHERE에서 COUNT는 안됨
# https://wikidocs.net/300578   서브쿼리 예제
SELECT ID, NAME, HOST_ID FROM PLACES
WHERE HOST_ID IN (SELECT HOST_ID FROM PLACES GROUP BY HOST_ID
HAVING COUNT(HOST_ID)&amp;gt;=2
      )
ORDER BY ID ASC&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;/p&gt;
&lt;pre id=&quot;code_1783260072558&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 다른 사람 풀이
# WITH 사용  
# WITH문이란?  이름을 가진 SubQuery를 정의한 후 사용하는 구문.

WITH HU AS (
    SELECT HOST_ID
    FROM PLACES
    GROUP BY host_id
    HAVING COUNT(ID) &amp;gt;= 2
)
-- 헤비 유저가 등록한 공간의 정보 모두 조회
SELECT *
FROM PLACES
WHERE HOST_ID IN (SELECT * FROM HU)
ORDER BY ID ASC&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브쿼리 , Group by , 조건 having count&lt;/p&gt;</description>
      <category>프로그래머스</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2121</guid>
      <comments>https://junworld.tistory.com/2121#entry2121comment</comments>
      <pubDate>Sun, 5 Jul 2026 23:01:28 +0900</pubDate>
    </item>
    <item>
      <title>(lv2) [3차] 파일명 정렬 [todo: 깔끔한 코드로 정리]</title>
      <link>https://junworld.tistory.com/2118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17686&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/17686&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1782656581652&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17686&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GUozB/dJMb86PchyM/R8nqXyLCc9NKckUq3QtMT0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/bQJ2Rf/dJMb8UHZNFD/jUne46tnOKrc5xwnFtL1r0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17686&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17686&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GUozB/dJMb86PchyM/R8nqXyLCc9NKckUq3QtMT0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/bQJ2Rf/dJMb8UHZNFD/jUne46tnOKrc5xwnFtL1r0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960');&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;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&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 data-ke-size=&quot;size16&quot;&gt;구현문제&lt;/p&gt;
&lt;pre id=&quot;code_1782656570882&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include&amp;lt;algorithm&amp;gt;
#include&amp;lt;iostream&amp;gt;

using namespace std;
//대소문자 구분x
//정렬 조건 h: 오름차순 , n: 숫자로만들어서 오름차순정렬

bool comp(string v, string t) {
    int t_size;
    if (v.size() &amp;gt; t.size()) {
        t_size = t.size();
    }
    else {
        t_size = v.size();
    }
    for (int idx = 0; idx &amp;lt; t_size; ++idx) {
        //대소문자 구분하지 않으면서도 기존 벡터 변형 없게 만들려고
        //임시값 ch1, ch2 생성
        char ch1 = v[idx];
        char ch2 = t[idx];
        //header 단계
        //idx별 문자가 동일할 때 먼저 숫자에 접근할 경우 true를 리턴해 낮은 은 값 정렬
        //둘다 숫자 접근 시 다음 단계인 숫자 비교로 넘어감
        if (ch1 &amp;gt;= '0' &amp;amp;&amp;amp; ch1 &amp;lt;= '9' &amp;amp;&amp;amp; ch2 &amp;gt;= '0' &amp;amp;&amp;amp; ch2 &amp;lt;= '9')break;
        if (ch1 &amp;gt;= '0' &amp;amp;&amp;amp; ch1 &amp;lt;= '9') {
            return true;
            break;
        }
        if (ch2 &amp;gt;= '0' &amp;amp;&amp;amp; ch2 &amp;lt;= '9') {
            return false;
            break;
        }
        if (ch1 &amp;gt;= 'a' &amp;amp;&amp;amp; ch1 &amp;lt;= 'z') {
            ch1 = 'A' + ch1 - 'a';
        }
        if (ch2 &amp;gt;= 'a' &amp;amp;&amp;amp; ch2 &amp;lt;= 'z') {
            ch2 = 'A' + ch2 - 'a';
        }

        if (ch1 &amp;lt; ch2) return true;
        if (ch1 &amp;gt; ch2) return false;
    }

    // if(v.size() &amp;lt; t.size()) return true;
     //if(v.size() &amp;gt; t.size()) return false;

// 숫자형태의 문자열 변수
    string vs = &quot;&quot;;
    string ts = &quot;&quot;;
    int flag = 0;
    int cnt = 0;
    //숫자는 5개 제한.
    for (int idx = 0; idx &amp;lt; v.size(); ++idx) {
        if (v[idx] &amp;gt;= '0' &amp;amp;&amp;amp; v[idx] &amp;lt;= '9') {
            vs += v[idx];
            cnt++;
            flag = 1;
        }
        else {
            if (flag == 1) {
                break;
            }
        }
        if (cnt &amp;gt;= 5)break;
    }
    flag = 0;
    cnt = 0;
    for (int idx = 0; idx &amp;lt; t.size(); ++idx) {
        if (t[idx] &amp;gt;= '0' &amp;amp;&amp;amp; t[idx] &amp;lt;= '9') {
            ts += t[idx];
            cnt++;
            flag = 1;
        }
        else {
            if (flag == 1) {
                break;
            }
        }
        if (cnt &amp;gt;= 5)break;
    }

//숫자 비교해서 오름차순 정렬 진행.
    int vVal = 0;
    int tVal = 0;
    vVal = stoi(vs);
    tVal = stoi(ts);
    if (vVal &amp;lt; tVal)return true;
    if (vVal &amp;gt; tVal)return false;
    return false;
}
vector&amp;lt;string&amp;gt; solution(vector&amp;lt;string&amp;gt; files) {

    stable_sort(files.begin(), files.end(), comp); // sort로 하면 안정적인 정렬이 아니라 함. stable_ 을 넣어야함
    vector&amp;lt;string&amp;gt; answer;
    for (int y = 0; y &amp;lt; files.size(); ++y) {
        for (int x = 0; x &amp;lt; files[y].size(); ++x) {
            cout &amp;lt;&amp;lt; files[y][x];
        }
        cout &amp;lt;&amp;lt; endl;
    }

    return files;
}

int main(void) {
    solution({ &quot;foo9.txt&quot;, &quot;foobar1.txt&quot; });
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로그래머스/코딩테스트</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2118</guid>
      <comments>https://junworld.tistory.com/2118#entry2118comment</comments>
      <pubDate>Sun, 28 Jun 2026 23:23:20 +0900</pubDate>
    </item>
    <item>
      <title>lv3) 정수 삼각형</title>
      <link>https://junworld.tistory.com/2117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43105&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/43105&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1782397583189&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43105&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pqQgN/dJMb9c9HWGu/2hUbGAfWjeyz9W4aOurl50/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/7rGmn/dJMb9llhaZx/KsYu5u5umIPwn1DLG1Mk2K/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/43105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pqQgN/dJMb9c9HWGu/2hUbGAfWjeyz9W4aOurl50/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/7rGmn/dJMb9llhaZx/KsYu5u5umIPwn1DLG1Mk2K/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960');&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;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&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 data-ke-size=&quot;size16&quot;&gt;#1 위-&amp;gt;아래&lt;/p&gt;
&lt;pre id=&quot;code_1782397593951&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int dp[501][501];
int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    dp[0][0] = triangle[0][0];
    int n = triangle.size();
    for (int y = 0; y &amp;lt; n - 1; ++y) {
        for (int x = 0; x &amp;lt; triangle[y].size(); ++x) {
            dp[y + 1][x] = max(dp[y + 1][x], dp[y][x] + triangle[y + 1][x]);
            dp[y + 1][x + 1] = max(dp[y + 1][x + 1], dp[y][x] + triangle[y + 1][x + 1]);
        }
    }
    int maxi = -1;
    for (int x = 0; x &amp;lt; 501; ++x) {
        if (maxi &amp;lt; dp[n - 1][x]) {
            maxi = dp[n - 1][x];
        }
    }
    return maxi;
}

int main() {
    solution({ {7},{3,8},{8,1,0},{2,7,4,4} ,{4,5,2,6,5} });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#2 아래-&amp;gt;위&lt;/p&gt;
&lt;pre id=&quot;code_1782397605692&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int dp[501][501];
int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    dp[0][0] = triangle[0][0];
    int n = triangle.size();
    for (int y = 1; y &amp;lt; n; ++y) {
        for (int x = 0; x &amp;lt; triangle[y].size()-1; ++x) {
            dp[y][x] = max(dp[y][x], dp[y-1][x] + triangle[y][x]);
            dp[y][x + 1] = max(dp[y][x + 1], dp[y-1][x] + triangle[y][x + 1]);
        }
    }
    int maxi = -1;
    for (int x = 0; x &amp;lt; 501; ++x) {
        if (maxi &amp;lt; dp[n - 1][x]) {
            maxi = dp[n - 1][x];
        }
    }
    return maxi;
}

int main() {
    solution({ {7},{3,8},{8,1,0},{2,7,4,4} ,{4,5,2,6,5} });
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로그래머스/코딩테스트</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2117</guid>
      <comments>https://junworld.tistory.com/2117#entry2117comment</comments>
      <pubDate>Thu, 25 Jun 2026 23:26:52 +0900</pubDate>
    </item>
    <item>
      <title>lv2 ) 완전범죄(틀림)   그리디 vs DP</title>
      <link>https://junworld.tistory.com/2110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/389480#&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/389480#&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리디로 접근 (실패)&lt;/p&gt;
&lt;pre id=&quot;code_1781792878796&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include&amp;lt;algorithm&amp;gt;
#include&amp;lt;iostream&amp;gt;
using namespace std;

//다중조건: 1. 2번째 원소 오름차순  2. 1번째 원소 오름차순 

bool comp1(vector&amp;lt;int&amp;gt; v, vector&amp;lt;int&amp;gt; tar){
    if(v[0] != tar[0])
        return v[0] &amp;gt; tar[0]; // A 내림차순
    return v[1] &amp;lt; tar[1];     // A 같으면 B 오름차순
} // v == tar 인 경우 v &amp;lt;= tar  -&amp;gt; true 반환. 이는 comp(x, x) = true가 되어버림에 따른 sort 알고리즘 내부 규칙에 위반 됨. 그렇기에 v &amp;lt; tar로 하자.
bool comp2(vector&amp;lt;int&amp;gt; v, vector&amp;lt;int&amp;gt; tar){
    if(v[1]!=tar[1]){ // B흔적 내림차순
        return v[1] &amp;lt; tar[1];
    }
    //2. 만약 B의 흔적이 같다면 A의 흔적(idx 0) 오름차순정렬
    return v[0] &amp;lt; tar[0];
}


int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; info, int n, int m) {
    int suml = 0; // return
    int sumr = 0;
    
    int flag = 1;
    sort(info.begin(), info.end(), comp1); // 정렬
    
    for(int y = 0; y &amp;lt; info.size(); ++y){
            if(flag == 1){
                sumr+=info[y][1];
                if(sumr &amp;gt;= m){
                    sumr-=info[y][1];
                    suml+=info[y][0];
                    if(suml&amp;gt;=n){
                        suml = -1;
                        break;
                    }
                    flag = 0;
                }
            }
            else{
                suml+=info[y][0];
                if(suml&amp;gt;=n){
                    suml = -1;
                    break;
                }
            }
    }
    int suml2 = 0, sumr2 = 0;
    flag = 1;
    sort(info.begin(), info.end(), comp2); // 정렬
    for(int y = 0; y &amp;lt; info.size(); ++y){
        cout&amp;lt;&amp;lt;info[y][0]&amp;lt;&amp;lt;&quot; &quot;&amp;lt;&amp;lt;info[y][1]&amp;lt;&amp;lt;endl;
            if(flag == 1){
                sumr2+=info[y][1];
                if(sumr2 &amp;gt;= m){
                    sumr2-=info[y][1];
                    suml2+=info[y][0];
                    if(suml2&amp;gt;=n)
                    {
                        suml2 = -1;
                        break;
                    }
                    flag = 0;
                }
            }
            else{
                suml2+=info[y][0];
                if(suml2&amp;gt;=n){
                    suml2 = -1;
                    break;
                }
            }
    }
    if(suml == -1)return suml2;
    if(suml2 == -1)return suml;
    if(suml&amp;gt;suml2)return suml2;
    return suml;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&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;letter-spacing: 0px;&quot;&gt;&quot;B에게 계속 몰아주다가 B가 한계에 가까워지면 그때부터 A에게 준다&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 &lt;b&gt;어떤 물건을 B에게 줄지&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;comp1) 1: A 내림차순&amp;nbsp; &amp;nbsp;2: B오름차순&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comp2) 1: B 내림차순&amp;nbsp; 2: A 오름차순&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;leftl 은 comp1 , leftl2는 comp2 방식 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘다 -1이면 -1리턴, 그렇지 않으면 둘 중 작은 값 리턴&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;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[그리디로는 안된다고 한다. 그런데 반례를 찾지 못했다.]&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리디 풀이는 이번 문제를 통과하지 못한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A의 흔적의 개수가 작은 순서대로 정렬을 하고 선택했을 때 A의 최솟값이 나오지 않기 때문이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(반례)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1, 2], [2, 3], [3, 2] [4, 7]&amp;nbsp; n = 7 m = 8 가정&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리디로 풀게 되면 정답은 6이 된다 A의 흔적 개수가 6이 될 때 14 - 7 = 7 B의 흔적 개수가 7이 되어 통과되지만&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답은 A의 흔적 개수가 4일 때 14 - 7 = 7이 되어서 조건을 만족하여 4가 정답이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://20240228.tistory.com/435&quot;&gt;https://20240228.tistory.com/435&lt;/a&gt; [지적 겸손함:티스토리]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;comp1 ) [4, 7], [3, 2], [2, 3], [1, 2]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comp2 ) [1, 2], [3, 2], [2, 3], [4, 7]&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;TC 답이 4나온다.(정답)&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 코드 반례&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;info = [[2,2],[3,2],[2,2],[1,1],[3,2]]
n = 9, m = 8
그리디: 3  &amp;rarr;  정답: 2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;&lt;b&gt;최적 배정:&lt;/b&gt; A B B B B&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A가 [2,2] 훔침 &amp;rarr; A흔적=2&lt;/li&gt;
&lt;li&gt;B가 나머지 4개 훔침 &amp;rarr; B흔적=2+2+1+2=7 &amp;lt; 8 ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 그리디는 정렬 후 &lt;b&gt;B흔적이 한계에 가까워질 때까지 B에게 몰아주다가&lt;/b&gt; 전환하는 방식이라, &lt;b&gt;첫 번째 물건을 A에게 줘야 최적&lt;/b&gt;이라는 걸 알 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;&quot;어떤 특정 물건을 A에게 줄지&quot;&lt;/b&gt; 를 정렬만으로 결정할 수 없는 게 본질적인 한계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;A에게 넘기는 물건&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;A흔적&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;B흔적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[2,2]&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;7 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[3,2]&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;7 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[2,2]&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;7 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[1,1]&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;8 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[3,2]&lt;/td&gt;
&lt;td style=&quot;width: 36.7442%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 38.0232%;&quot;&gt;7 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;comp1) 1: A 내림차순&amp;nbsp; &amp;nbsp;2: B오름차순&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;comp2) 1: B 내림차순&amp;nbsp; 2: A 오름차순&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두가지 정렬을 적용해서 둘 중 하나의 A값이 작으면 그 값을 리턴한다. 다만 그 작은 값이 -1이면 둘다 -1이 아니라는 가정 하에 큰 값을 리턴한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 위와 같은 반례처럼 해당 조합(comp1, comp2 정렬 후 선택 전략)이라도, 즉 어떤 정렬 순서로도 (&quot;정렬 기준을 2, 3개 ... 10개로 늘려도) &quot;운 좋게 맞는 케이스&quot;가 늘어날 뿐, 항상 최적을 보장하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최적을 항상 보장하려면 DP를 사용해야한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그리디 VS DP&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;91&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리디 &amp;sub; DP&lt;/b&gt; 관계&lt;/p&gt;
&lt;p data-end=&quot;91&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;많은 경우 그리디로 해결 가능한 문제는 DP로도 해결 가능하다.&lt;br /&gt;반면 DP로 해결 가능한 문제 중에는 그리디로 해결할 수 없는 문제도 많다.&lt;br /&gt;따라서 실전에서는 DP가 그리디보다 더 범용적인 접근으로 여겨진다.&lt;/p&gt;
&lt;p data-end=&quot;91&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;185&quot; data-start=&quot;93&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;210&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;정확히는 다음과 같이 이해하는 것이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;334&quot; data-start=&quot;212&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;253&quot; data-start=&quot;212&quot; data-section-id=&quot;1bqrcly&quot;&gt;그리디로 최적해가 보장되는 문제들은 대체로 DP적으로도 표현 가능하다. (그리디는 항상 &amp;ldquo;왜 최적인가&amp;rdquo;에 대한 증명이 중요하다.)&lt;/li&gt;
&lt;li data-end=&quot;334&quot; data-start=&quot;254&quot; data-section-id=&quot;1suo5pz&quot;&gt;하지만 DP가 필요한 문제 중 상당수는 greedy choice property(탐욕 선택 성질)가 성립하지 않아 그리디로 해결할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;349&quot; data-start=&quot;336&quot; data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같다.&lt;/p&gt;
&lt;p data-end=&quot;369&quot; data-start=&quot;351&quot; data-ke-size=&quot;size16&quot;&gt;그리디와 DP 모두 가능한 문제:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;433&quot; data-start=&quot;370&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;387&quot; data-start=&quot;370&quot; data-section-id=&quot;5ingug&quot;&gt;동전 문제(특정 화폐 체계)&lt;/li&gt;
&lt;li data-end=&quot;398&quot; data-start=&quot;388&quot; data-section-id=&quot;pzu238&quot;&gt;활동 선택 문제&lt;/li&gt;
&lt;li data-end=&quot;409&quot; data-start=&quot;399&quot; data-section-id=&quot;1ljzv9l&quot;&gt;최소 신장 트리&lt;/li&gt;
&lt;li data-end=&quot;433&quot; data-start=&quot;410&quot; data-section-id=&quot;12jzygc&quot;&gt;최단 경로(비음수 가중치의 다익스트라)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;461&quot; data-start=&quot;435&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DP는 가능&lt;/b&gt;하지만 그리디는 불가능한 대표 문제:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;504&quot; data-start=&quot;462&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;473&quot; data-start=&quot;462&quot; data-section-id=&quot;m1z7en&quot;&gt;0/1 배낭 문제&lt;/li&gt;
&lt;li data-end=&quot;485&quot; data-start=&quot;474&quot; data-section-id=&quot;4kmqum&quot;&gt;LIS 일반 DP&lt;/li&gt;
&lt;li data-end=&quot;496&quot; data-start=&quot;486&quot; data-section-id=&quot;1rikfdg&quot;&gt;행렬 곱셈 순서&lt;/li&gt;
&lt;li data-end=&quot;504&quot; data-start=&quot;497&quot; data-section-id=&quot;oqzjml&quot;&gt;편집 거리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;529&quot; data-start=&quot;506&quot; data-ke-size=&quot;size16&quot;&gt;특히 0/1 배낭 문제는 대표적인 예시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;597&quot; data-start=&quot;531&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;570&quot; data-start=&quot;531&quot; data-section-id=&quot;1sh2pac&quot;&gt;&amp;ldquo;가성비 좋은 물건부터 담자&amp;rdquo; 같은 단순 그리디는 반례가 존재한다.&lt;/li&gt;
&lt;li data-end=&quot;597&quot; data-start=&quot;571&quot; data-section-id=&quot;1cwgyj7&quot;&gt;하지만 DP는 항상 최적해를 구할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;622&quot; data-start=&quot;599&quot; data-ke-size=&quot;size16&quot;&gt;실전에서는 다음처럼 접근하는 경우가 많다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;824&quot; data-start=&quot;624&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;624&quot; data-section-id=&quot;ppw9m8&quot;&gt;그리디가 바로 보이지 않으면 DP를 먼저 고려하는 것이 안전한 경우가 많다.&lt;/li&gt;
&lt;li data-end=&quot;751&quot; data-start=&quot;669&quot; data-section-id=&quot;13xjfa7&quot;&gt;그리디는 반드시 교환 논법(exchange argument), greedy choice property, 반례 검증 등을 통해 정당화해야 한다.&lt;/li&gt;
&lt;li data-end=&quot;795&quot; data-start=&quot;752&quot; data-section-id=&quot;1smue5i&quot;&gt;반면 DP는 상태 정의와 점화식만 올바르면 최적성을 구성하기 비교적 쉽다.&lt;/li&gt;
&lt;li data-end=&quot;824&quot; data-start=&quot;796&quot; data-section-id=&quot;usszh0&quot;&gt;대신 시간과 메모리를 더 사용하는 경우가 많다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;868&quot; data-start=&quot;826&quot; data-ke-size=&quot;size16&quot;&gt;또한 &amp;ldquo;DP는 느리지만 정확성을 보장한다&amp;rdquo;라는 표현은 약간 수정이 필요하다.&lt;/p&gt;
&lt;p data-end=&quot;893&quot; data-start=&quot;870&quot; data-ke-size=&quot;size16&quot;&gt;정확성은 알고리즘 자체의 증명 문제이므로:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;934&quot; data-start=&quot;894&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;912&quot; data-start=&quot;894&quot; data-section-id=&quot;1pp4he2&quot;&gt;잘 설계된 그리디는 정확하다.&lt;/li&gt;
&lt;li data-end=&quot;934&quot; data-start=&quot;913&quot; data-section-id=&quot;133fmah&quot;&gt;잘못 설계된 DP는 틀릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;968&quot; data-start=&quot;936&quot; data-ke-size=&quot;size16&quot;&gt;따라서 일반적으로는 다음처럼 정리하는 것이 가장 적절하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1050&quot; data-start=&quot;970&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1004&quot; data-start=&quot;970&quot; data-section-id=&quot;700oao&quot;&gt;그리디: 빠르고 구현이 간단하지만 최적성 증명이 필요하다.&lt;/li&gt;
&lt;/ul&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;&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-end=&quot;47&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 전형적인 &lt;b&gt;0/1 배낭(Knapsack) 계열 DP&lt;/b&gt;와 가장 흡사하다.&lt;/p&gt;
&lt;p data-end=&quot;56&quot; data-start=&quot;49&quot; data-ke-size=&quot;size16&quot;&gt;특히 느낌은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;132&quot; data-start=&quot;58&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;75&quot; data-start=&quot;58&quot; data-section-id=&quot;rzoqc7&quot;&gt;물건마다 선택지가 2개 있고&lt;/li&gt;
&lt;li data-end=&quot;96&quot; data-start=&quot;76&quot; data-section-id=&quot;16d3xh8&quot;&gt;누적 비용(흔적)을 관리해야 하며&lt;/li&gt;
&lt;li data-end=&quot;116&quot; data-start=&quot;97&quot; data-section-id=&quot;cxmvbn&quot;&gt;제한 조건을 넘지 않도록 하면서&lt;/li&gt;
&lt;li data-end=&quot;132&quot; data-start=&quot;117&quot; data-section-id=&quot;14432jx&quot;&gt;어떤 값을 최소화/최대화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;152&quot; data-start=&quot;134&quot; data-ke-size=&quot;size16&quot;&gt;한다는 점에서 거의 배낭 구조다.&lt;/p&gt;
&lt;p data-end=&quot;166&quot; data-start=&quot;154&quot; data-ke-size=&quot;size16&quot;&gt;구체적으로 대응시키면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;318&quot; data-start=&quot;168&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;186&quot; data-start=&quot;168&quot; data-section-id=&quot;1ldfxmg&quot;&gt;물건 i&lt;br /&gt;&amp;rarr; 배낭의 아이템&lt;/li&gt;
&lt;li data-end=&quot;219&quot; data-start=&quot;188&quot; data-section-id=&quot;19kwmux&quot;&gt;A가 훔친다 / B가 훔친다&lt;br /&gt;&amp;rarr; 아이템 선택 방식&lt;/li&gt;
&lt;li data-end=&quot;260&quot; data-start=&quot;221&quot; data-section-id=&quot;19eisuw&quot;&gt;A 흔적, B 흔적&lt;br /&gt;&amp;rarr; 무게(weight) 혹은 비용(cost)&lt;/li&gt;
&lt;li data-end=&quot;295&quot; data-start=&quot;262&quot; data-section-id=&quot;2om6qt&quot;&gt;경찰에 안 잡혀야 함&lt;br /&gt;&amp;rarr; 용량 제한(capacity)&lt;/li&gt;
&lt;li data-end=&quot;318&quot; data-start=&quot;297&quot; data-section-id=&quot;bn4pjw&quot;&gt;A 흔적 최소화&lt;br /&gt;&amp;rarr; 최적화 목표&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;344&quot; data-start=&quot;320&quot; data-ke-size=&quot;size16&quot;&gt;특히 이 문제는 다음 형태와 매우 비슷하다.&lt;/p&gt;
&lt;blockquote data-end=&quot;374&quot; data-start=&quot;346&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;374&quot; data-start=&quot;348&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;한쪽 비용 제한 안에서 다른 쪽 비용 최소화&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;378&quot; data-start=&quot;376&quot; data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;423&quot; data-start=&quot;380&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;398&quot; data-start=&quot;380&quot; data-section-id=&quot;nzdznm&quot;&gt;B 흔적은 m 미만이어야 하고&lt;/li&gt;
&lt;li data-end=&quot;412&quot; data-start=&quot;399&quot; data-section-id=&quot;d6wtml&quot;&gt;그 조건을 만족하면서&lt;/li&gt;
&lt;li data-end=&quot;423&quot; data-start=&quot;413&quot; data-section-id=&quot;1asd2mw&quot;&gt;A 흔적 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;431&quot; data-start=&quot;425&quot; 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;&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리디 VS&amp;nbsp; DP&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;a href=&quot;https://brynn-park.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://brynn-park.tistory.com/41&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781826900186&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;Greedy vs. DP(Dynamic Programming)&quot; data-og-description=&quot;최적의 솔루션을 도출하는 두 가지 알고리즘-Greedy, DP-에 대해서 차이점을 중심으로 알아본다. 스스로 헷갈려서 차이점을 명확히 구분하고 공부하기 위해 적는 포스팅이다.&amp;nbsp;Greedy AlgorithmGreedy Algo&quot; data-og-host=&quot;brynn-park.tistory.com&quot; data-og-source-url=&quot;https://brynn-park.tistory.com/41&quot; data-og-url=&quot;https://brynn-park.tistory.com/41&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Tv32G/dJMb8PGFsh4/UjN1jkZZkFgW3vesHU4Ugk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/OmHWB/dJMb9ia0ihn/fLfb2PdOYM0BNDAAHQyaw0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/caPznS/dJMb9jgGwKW/8Z9nrQ9PjCMOCeA1cfFjw1/img.jpg?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500&quot;&gt;&lt;a href=&quot;https://brynn-park.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brynn-park.tistory.com/41&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Tv32G/dJMb8PGFsh4/UjN1jkZZkFgW3vesHU4Ugk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/OmHWB/dJMb9ia0ihn/fLfb2PdOYM0BNDAAHQyaw0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/caPznS/dJMb9jgGwKW/8Z9nrQ9PjCMOCeA1cfFjw1/img.jpg?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500');&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;Greedy vs. DP(Dynamic Programming)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최적의 솔루션을 도출하는 두 가지 알고리즘-Greedy, DP-에 대해서 차이점을 중심으로 알아본다. 스스로 헷갈려서 차이점을 명확히 구분하고 공부하기 위해 적는 포스팅이다.&amp;nbsp;Greedy AlgorithmGreedy Algo&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;brynn-park.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 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;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;74&quot; data-start=&quot;53&quot; data-section-id=&quot;8a7mrm&quot;&gt;그리 vs DP 구분법 (실전용)&lt;/h1&gt;
&lt;h2 data-end=&quot;97&quot; data-start=&quot;76&quot; data-section-id=&quot;vaqh3j&quot; data-ke-size=&quot;size26&quot;&gt;1. 그리디를 먼저 의심하는 경우&lt;/h2&gt;
&lt;p data-end=&quot;121&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;다음 중 하나라도 강하면 그리디 후보다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;268&quot; data-start=&quot;123&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;157&quot; data-start=&quot;123&quot; data-section-id=&quot;144zrxt&quot;&gt;현재 선택이 &amp;ldquo;전체 최적&amp;rdquo;에 영향을 거의 안 주는 느낌이다&lt;/li&gt;
&lt;li data-end=&quot;178&quot; data-start=&quot;158&quot; data-section-id=&quot;r2n28r&quot;&gt;정렬 후 규칙적으로 풀릴 것 같다&lt;/li&gt;
&lt;li data-end=&quot;205&quot; data-start=&quot;179&quot; data-section-id=&quot;tycqgr&quot;&gt;&amp;ldquo;지금 가장 좋은 선택&amp;rdquo;이 자연스럽게 보인다&lt;/li&gt;
&lt;li data-end=&quot;234&quot; data-start=&quot;206&quot; data-section-id=&quot;1rv0ahu&quot;&gt;선택이 이후 상태를 크게 복잡하게 만들지 않는다&lt;/li&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;235&quot; data-section-id=&quot;ltkt0b&quot;&gt;교환해도 결과가 유지될 것 같다 (교환 논법 가능 느낌)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;277&quot; data-start=&quot;270&quot; data-ke-size=&quot;size16&quot;&gt;  한마디:&lt;/p&gt;
&lt;blockquote data-end=&quot;307&quot; data-start=&quot;278&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;307&quot; data-start=&quot;280&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;지금 최선 선택 = 전체 최선&amp;rdquo; 느낌이면 그리디&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;312&quot; data-start=&quot;309&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;332&quot; data-start=&quot;314&quot; data-section-id=&quot;6lx9l0&quot; data-ke-size=&quot;size26&quot;&gt;2. DP를 써야 하는 경우&lt;/h2&gt;
&lt;p data-end=&quot;346&quot; data-start=&quot;334&quot; data-ke-size=&quot;size16&quot;&gt;다음 특징이면 DP다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;480&quot; data-start=&quot;348&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;373&quot; data-start=&quot;348&quot; data-section-id=&quot;1l3kgww&quot;&gt;현재 선택이 미래 선택에 영향을 크게 준다&lt;/li&gt;
&lt;li data-end=&quot;405&quot; data-start=&quot;374&quot; data-section-id=&quot;f14agx&quot;&gt;&amp;ldquo;어떤 선택을 하느냐에 따라 상태가 완전히 달라진다&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;430&quot; data-start=&quot;406&quot; data-section-id=&quot;1hxu3y3&quot;&gt;경우의 수가 너무 많아서 완전탐색 불가능&lt;/li&gt;
&lt;li data-end=&quot;451&quot; data-start=&quot;431&quot; data-section-id=&quot;1e5bslg&quot;&gt;같은 상태가 여러 경로로 반복된다&lt;/li&gt;
&lt;li data-end=&quot;480&quot; data-start=&quot;452&quot; data-section-id=&quot;x5re33&quot;&gt;&amp;ldquo;~까지의 최적값 / 경우의 수&amp;rdquo; 형태가 보인다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;489&quot; data-start=&quot;482&quot; data-ke-size=&quot;size16&quot;&gt;  한마디:&lt;/p&gt;
&lt;blockquote data-end=&quot;526&quot; data-start=&quot;490&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;526&quot; data-start=&quot;492&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;선택에 따라 미래가 갈리고, 같은 상태가 반복된다&amp;rdquo; &amp;rarr; DP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;531&quot; data-start=&quot;528&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;560&quot; data-start=&quot;533&quot; data-section-id=&quot;25hlro&quot; data-ke-size=&quot;size26&quot;&gt;3. 가장 중요한 핵심 기준 (시험용 1줄)&lt;/h2&gt;
&lt;blockquote data-end=&quot;627&quot; data-start=&quot;562&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;627&quot; data-start=&quot;564&quot; data-ke-size=&quot;size16&quot;&gt;그리디: &amp;ldquo;지금 선택이 곧 답이 된다&amp;rdquo;가 증명 가능&lt;br /&gt;DP: &amp;ldquo;모든 선택을 고려해야 최적을 보장할 수 있다&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;632&quot; data-start=&quot;629&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;651&quot; data-start=&quot;634&quot; data-section-id=&quot;1oh14rb&quot; data-ke-size=&quot;size26&quot;&gt;4. 빠른 판단 체크리스트&lt;/h2&gt;
&lt;h3 data-end=&quot;663&quot; data-start=&quot;653&quot; data-section-id=&quot;1pqwpry&quot; data-ke-size=&quot;size23&quot;&gt;그리디 체크&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;711&quot; data-start=&quot;664&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;679&quot; data-start=&quot;664&quot; data-section-id=&quot;1qug6ug&quot;&gt;정렬하면 풀릴 것 같다?&lt;/li&gt;
&lt;li data-end=&quot;692&quot; data-start=&quot;680&quot; data-section-id=&quot;1ug3x5w&quot;&gt;반례 찾기 어렵다?&lt;/li&gt;
&lt;li data-end=&quot;711&quot; data-start=&quot;693&quot; data-section-id=&quot;18e5nmb&quot;&gt;선택 기준이 하나로 정해진다?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;729&quot; data-start=&quot;713&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; YES 많으면 그리디 의심&lt;/p&gt;
&lt;hr data-end=&quot;734&quot; data-start=&quot;731&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;745&quot; data-start=&quot;736&quot; data-section-id=&quot;6ygj1m&quot; data-ke-size=&quot;size23&quot;&gt;DP 체크&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;807&quot; data-start=&quot;746&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;769&quot; data-start=&quot;746&quot; data-section-id=&quot;1vvuwyu&quot;&gt;선택이 2개 이상이고 이후 영향이 크다&lt;/li&gt;
&lt;li data-end=&quot;787&quot; data-start=&quot;770&quot; data-section-id=&quot;1hj0h3u&quot;&gt;상태를 저장해야 할 것 같다&lt;/li&gt;
&lt;li data-end=&quot;807&quot; data-start=&quot;788&quot; data-section-id=&quot;1aw6gao&quot;&gt;&amp;ldquo;부분 문제 반복&amp;rdquo; 느낌이 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;821&quot; data-start=&quot;809&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; YES 많으면 DP&lt;/p&gt;
&lt;hr data-end=&quot;826&quot; data-start=&quot;823&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;839&quot; data-start=&quot;828&quot; data-section-id=&quot;1iaebfy&quot; data-ke-size=&quot;size26&quot;&gt;5. 핵심 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;916&quot; data-start=&quot;841&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;864&quot; data-start=&quot;841&quot; data-section-id=&quot;gmbjw5&quot;&gt;그리디 = &amp;ldquo;선택 1개로 끝나는 구조&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;890&quot; data-start=&quot;865&quot; data-section-id=&quot;k66cg0&quot;&gt;DP = &amp;ldquo;선택을 다 저장해야 하는 구조&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;891&quot; data-section-id=&quot;1lp5ne4&quot;&gt;둘 다 아니면 보통 그래프/탐색/비트마스크&lt;/li&gt;
&lt;/ul&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;h1 data-end=&quot;51&quot; data-start=&quot;38&quot; data-section-id=&quot;k4hshf&quot;&gt;해당 문제가 DP 쪽인지 체크&lt;/h1&gt;
&lt;h2 data-end=&quot;75&quot; data-start=&quot;53&quot; data-section-id=&quot;20vvio&quot; data-ke-size=&quot;size26&quot;&gt;1. 선택이 누적된다 &amp;rarr; DP 신호&lt;/h2&gt;
&lt;p data-end=&quot;90&quot; data-start=&quot;76&quot; data-ke-size=&quot;size16&quot;&gt;각 물건마다 선택이 있음:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;127&quot; data-start=&quot;92&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;109&quot; data-start=&quot;92&quot; data-section-id=&quot;1xfkrn2&quot;&gt;A가 훔침 &amp;rarr; A 흔적 증가&lt;/li&gt;
&lt;li data-end=&quot;127&quot; data-start=&quot;110&quot; data-section-id=&quot;9pg3qm&quot;&gt;B가 훔침 &amp;rarr; B 흔적 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;169&quot; data-start=&quot;129&quot; data-ke-size=&quot;size16&quot;&gt;이 선택이 계속 누적돼서&lt;br /&gt;&lt;b&gt;현재 선택이 미래 가능 여부를 바꾼다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;183&quot; data-start=&quot;171&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 전형적인 DP 조건&lt;/p&gt;
&lt;h2 data-end=&quot;236&quot; data-start=&quot;190&quot; data-section-id=&quot;cskhl0&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;236&quot; data-start=&quot;190&quot; data-section-id=&quot;cskhl0&quot; data-ke-size=&quot;size26&quot;&gt;2. &amp;ldquo;지금 A/B 중 더 작은 것&amp;rdquo;이 항상 최적이 아님 &amp;rarr; 그리디 실패 구조&lt;/h2&gt;
&lt;p data-end=&quot;244&quot; data-start=&quot;238&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;301&quot; data-start=&quot;246&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;267&quot; data-start=&quot;246&quot; data-section-id=&quot;pwgqph&quot;&gt;지금은 A로 하면 흔적이 적어 보임&lt;/li&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;268&quot; data-section-id=&quot;14tohl7&quot;&gt;하지만 나중에 A 제한(n)에 빨리 도달하면 전체가 터짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;305&quot; data-start=&quot;303&quot; data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;p data-end=&quot;305&quot; data-start=&quot;303&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px; font-family: 'Noto Serif KR', serif; text-align: center;&quot;&gt;지역 최적 선택 &amp;ne; 전체 최적&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;338&quot; data-start=&quot;327&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그리디 구조 붕괴&lt;/p&gt;
&lt;h2 data-end=&quot;361&quot; data-start=&quot;345&quot; data-section-id=&quot;21m832&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;361&quot; data-start=&quot;345&quot; data-section-id=&quot;21m832&quot; data-ke-size=&quot;size26&quot;&gt;3. 상태가 2개 축이다&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;382&quot; data-start=&quot;363&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;372&quot; data-start=&quot;363&quot; data-section-id=&quot;1bffzx5&quot;&gt;A 누적 흔적&lt;/li&gt;
&lt;li data-end=&quot;382&quot; data-start=&quot;373&quot; data-section-id=&quot;1ub7uqi&quot;&gt;B 누적 흔적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;391&quot; data-start=&quot;384&quot; data-ke-size=&quot;size16&quot;&gt;이건 사실상:&lt;/p&gt;
&lt;p data-end=&quot;391&quot; data-start=&quot;384&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2차원 자원 관리 문제&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;409&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 전형적인 DP (knapsack 변형)&lt;/p&gt;
&lt;h2 data-end=&quot;465&quot; data-start=&quot;439&quot; data-section-id=&quot;czeqsi&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;465&quot; data-start=&quot;439&quot; data-section-id=&quot;czeqsi&quot; data-ke-size=&quot;size26&quot;&gt;4. &amp;ldquo;남은 선택이 미래 가능성을 결정&amp;rdquo;함&lt;/h2&gt;
&lt;p data-end=&quot;475&quot; data-start=&quot;467&quot; data-ke-size=&quot;size16&quot;&gt;이게 핵심인데:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;535&quot; data-start=&quot;477&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;507&quot; data-start=&quot;477&quot; data-section-id=&quot;s9b08a&quot;&gt;어떤 물건을 A로 선택하면&lt;br /&gt;&amp;rarr; B 여유가 늘어남&lt;/li&gt;
&lt;li data-end=&quot;535&quot; data-start=&quot;508&quot; data-section-id=&quot;1lxv0ia&quot;&gt;반대로 B로 선택하면&lt;br /&gt;&amp;rarr; A 여유가 늘어남&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;559&quot; data-start=&quot;537&quot; data-ke-size=&quot;size16&quot;&gt;즉 선택이 독립이 아니라 서로 얽혀 있음&lt;/p&gt;
&lt;p data-end=&quot;576&quot; data-start=&quot;561&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그리디 성립 거의 불가능&lt;/p&gt;
&lt;h1 data-end=&quot;600&quot; data-start=&quot;583&quot; data-section-id=&quot;19hn3zr&quot;&gt;그리디 체크는 거의 안 통함&lt;/h1&gt;
&lt;p data-end=&quot;626&quot; data-start=&quot;602&quot; data-ke-size=&quot;size16&quot;&gt;그리디가 맞으려면 보통 이런 게 있어야 함:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;666&quot; data-start=&quot;628&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;638&quot; data-start=&quot;628&quot; data-section-id=&quot;nv2pkx&quot;&gt;정렬 기준 하나&lt;/li&gt;
&lt;li data-end=&quot;649&quot; data-start=&quot;639&quot; data-section-id=&quot;6edsf9&quot;&gt;선택 기준 하나&lt;/li&gt;
&lt;li data-end=&quot;666&quot; data-start=&quot;650&quot; data-section-id=&quot;x4rkhq&quot;&gt;선택이 미래를 망치지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;677&quot; data-start=&quot;668&quot; data-ke-size=&quot;size16&quot;&gt;근데 이 문제는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;725&quot; data-start=&quot;679&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;694&quot; data-start=&quot;679&quot; data-section-id=&quot;aziafg&quot;&gt;기준이 2개 (A, B)&lt;/li&gt;
&lt;li data-end=&quot;710&quot; data-start=&quot;695&quot; data-section-id=&quot;1g404l4&quot;&gt;선택이 미래 제약을 바꿈&lt;/li&gt;
&lt;li data-end=&quot;725&quot; data-start=&quot;711&quot; data-section-id=&quot;1112kpc&quot;&gt;trade-off 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;744&quot; data-start=&quot;727&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그리디 조건 거의 전부 깨짐&lt;/p&gt;
&lt;h1 data-end=&quot;755&quot; data-start=&quot;751&quot; data-section-id=&quot;yi4awm&quot;&gt;결론&lt;/h1&gt;
&lt;h1 data-end=&quot;755&quot; data-start=&quot;751&quot; data-section-id=&quot;yi4awm&quot;&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;이 문제는 그리디 체크보다 DP 체크가 훨씬 강한 전형적인 2차원 DP 문제다.&lt;/span&gt;&lt;/h1&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;style5&quot; /&gt;
&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-section-id=&quot;yi4awm&quot; data-start=&quot;751&quot; data-end=&quot;755&quot;&gt;시간제한선택&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 info 길이 최대 40이니까:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;2^40 = 약 1,099,511,627,776 = 약 1조&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10^8 기준으로 &lt;b&gt;1만 배&lt;/b&gt; 넘어서 완전히 시간초과다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준 정리:&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;크기&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;가능한 알고리즘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 20&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;2^n 브루트포스 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 40&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;브루트포스 불가, DP/분할정복&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 100&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;O(n^3) 이하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 1000&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;O(n^2) 이하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 100,000&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;O(n log n) 이하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;n &amp;le; 1,000,000&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;O(n) 이하&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래머스/코딩테스트</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2110</guid>
      <comments>https://junworld.tistory.com/2110#entry2110comment</comments>
      <pubDate>Fri, 19 Jun 2026 00:26:10 +0900</pubDate>
    </item>
    <item>
      <title>lv2) 멀리 뛰기</title>
      <link>https://junworld.tistory.com/2109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12914&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/12914&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1781619343529&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include&amp;lt;iostream&amp;gt;
using namespace std;
long long answer= 0;
void dfs(int sum, int lv, int limit){
    if(lv &amp;lt;= limit){
        if(sum == limit){
            answer++;
            return;
        }
        if(lv == limit)return;
    }
    if(sum == limit){
        answer++;
       return;     }
    if(sum &amp;gt; limit)return;
    for(int i = 1 ; i &amp;lt;=2;++i){
        dfs(sum + i, lv+1, limit);
    }
}
long long solution(int n) {

   // dfs(0, 0, n);   시간초과 
    //dp 점화식: dp[n] = dp[n-1] + dp[n-2];
   vector&amp;lt;long long&amp;gt;dp(n+1);
    if(n == 1)return 1;
    if(n == 2) return 2;
   dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
     for(int idx = 3; idx &amp;lt;= n ;++idx){
         dp[idx] = (dp[idx-1] + dp[idx-2]) % 1234567;
    }   
    
    return dp[n];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- dfs로 하면 시간 초과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- dp로 진행해야 하며, dp[idx] 저장 때마다 %1234567 진행해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 조건만 보면 리턴 시에만 %1234567 을 하라했는데, 그게 아니었다. 매 계산 저장 전 modular 연산을 통해 오버플로우 방지를 해야한다. long long 타입이어도 피보나치 수열의 값 범위를 온전히 담을 수 없기 때문이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;피보나치 수열(이 문제의 점화식)은 &lt;span data-index-in-node=&quot;20&quot; data-math=&quot;n&quot;&gt;n&lt;/span&gt;이 커질 수록 숫자가 기하급수적으로 증가한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제한 사항을 보면 &lt;span data-index-in-node=&quot;10&quot; data-math=&quot;n&quot;&gt;n&lt;/span&gt;은 최대 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;5,0,0&quot;&gt;2,000&lt;/b&gt;까지 주어진다.&lt;/li&gt;
&lt;li&gt;long long 타입이 아무리 큰 수를 담을 수 있다고 해도, 피보나치 수열의 90번째 항만 넘어가도 표현할 수 있는 범위를 초과해 버린다. 2,000번째 항은 상상할 수 없을 정도로 큰 수가 되어 쓰레기 값이 담기게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래머스/코딩테스트</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2109</guid>
      <comments>https://junworld.tistory.com/2109#entry2109comment</comments>
      <pubDate>Tue, 16 Jun 2026 23:19:08 +0900</pubDate>
    </item>
    <item>
      <title>비트마스킹 관련 연습 (7다)</title>
      <link>https://junworld.tistory.com/2098</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#1 부분 집합 풀기&lt;/p&gt;
&lt;pre id=&quot;code_1780477114645&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;iostream&amp;gt;

using namespace std;

int main(void) {
	int n = 4;
	for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
		cout &amp;lt;&amp;lt; &quot;{ &quot;;
		for (int idx = 0; idx &amp;lt; n; ++idx) {
			if ((mask &amp;gt;&amp;gt; idx) &amp;amp; 1) {
				cout &amp;lt;&amp;lt; idx + 1 &amp;lt;&amp;lt; &quot; &quot;;
			}
		}	
		cout &amp;lt;&amp;lt; &quot;}\n&quot;;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부분집합&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;집합에서 고를 수 있는 &lt;b&gt;모든 경우&lt;/b&gt; (아무것도 안 고르는 것 포함)&lt;/li&gt;
&lt;li&gt;{1,2,3} 의 부분집합 &amp;rarr; {}, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3} &amp;rarr; 총 2^n개&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조합&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n개 중에서 &lt;b&gt;정확히 k개&lt;/b&gt; 고르는 경우&lt;/li&gt;
&lt;li&gt;{1,2,3} 에서 2개 고르는 조합 &amp;rarr; {1,2}, {1,3}, {2,3} &amp;rarr; 총 nCk개&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계로 보면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;부분집합 = k=0인 조합 + k=1인 조합 + k=2인 조합 + ... + k=n인 조합&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junworld.tistory.com/2097&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://junworld.tistory.com/2097&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힌트스테이지 문제에서는 &quot;번들을 살지 말지&quot; 각각 독립적으로 결정하니까 &lt;b&gt;부분집합&lt;/b&gt; 탐색이고, 만약 &quot;n-1개 스테이지 중 정확히 3개만 번들을 산다&quot;는 조건이 있었다면 &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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 원소의 합이 10이 나오는 부분 집합 개수 구하기&lt;/p&gt;
&lt;pre id=&quot;code_1780484391283&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;iostream&amp;gt;
#include&amp;lt;string&amp;gt;

using namespace std;

int solve(int n, int arr[]) {
    int count = 0;
    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        int sum = 0;
        int cnt = 0;
        string subset = &quot;{ &quot;;

        for (int idx = 0; idx &amp;lt; n; idx++) {
            if ((mask &amp;gt;&amp;gt; idx) &amp;amp; 1) {
                sum += arr[idx];
                cnt++;
                subset += to_string(arr[idx]) + &quot; &quot;;
            }
        }

        if (cnt == 2 &amp;amp;&amp;amp; sum == 7) {
            cout &amp;lt;&amp;lt; subset &amp;lt;&amp;lt; &quot;}\n&quot;;
            count++;
        }
    }
    return count;
}

int main() {
    int n = 8;
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    cout &amp;lt;&amp;lt; &quot;개수: &quot; &amp;lt;&amp;lt; solve(n, arr) &amp;lt;&amp;lt; &quot;\n&quot;;
    return 0;
}&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;&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;1단계 - 비트 연산 기본&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
    int a = 5; // 0101
    int b = 3; // 0011

    cout &amp;lt;&amp;lt; (a &amp;amp; b) &amp;lt;&amp;lt; &quot;\n&quot;;  // AND  &amp;rarr; 0001 = 1
    cout &amp;lt;&amp;lt; (a | b) &amp;lt;&amp;lt; &quot;\n&quot;;  // OR   &amp;rarr; 0111 = 7
    cout &amp;lt;&amp;lt; (a ^ b) &amp;lt;&amp;lt; &quot;\n&quot;;  // XOR  &amp;rarr; 0110 = 6
    cout &amp;lt;&amp;lt; (a &amp;lt;&amp;lt; 1) &amp;lt;&amp;lt; &quot;\n&quot;; // 왼쪽 시프트 &amp;rarr; 1010 = 10
    cout &amp;lt;&amp;lt; (a &amp;gt;&amp;gt; 1) &amp;lt;&amp;lt; &quot;\n&quot;; // 오른쪽 시프트 &amp;rarr; 0010 = 2
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;&lt;b&gt;2단계 - 특정 비트 켜기/끄기/확인&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include&amp;lt;iostream&amp;gt;
using namespace std;

void printBit(int mask, int size = 5) {
    // 상위 비트부터 출력 (앞의 0도 포함)
    for (int i = size - 1; i &amp;gt;= 0; i--) {
        cout &amp;lt;&amp;lt; ((mask &amp;gt;&amp;gt; i) &amp;amp; 1);
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

bool checkBit(int mask, int idx) {
    if ((mask &amp;gt;&amp;gt; idx) &amp;amp; 1) {
        cout &amp;lt;&amp;lt; &quot;true\n&quot;;
        return true;
    }
    else {
        cout &amp;lt;&amp;lt; &quot;false\n&quot;;
        return false;
    }
}

int main(void) {
    int mask = 0;

    // 0번째, 4번째 비트 켜기
    mask |= (1 &amp;lt;&amp;lt; 0);
    mask |= (1 &amp;lt;&amp;lt; 4);
    printBit(mask);   // 10001

    // 0번째 비트 끄기
    mask &amp;amp;= ~(1 &amp;lt;&amp;lt; 0);
    printBit(mask);   // 10000

    // 4번째 비트 확인
    checkBit(mask, 4); // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계 - 부분집합 전체 출력&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;int main() {
    vector&amp;lt;int&amp;gt; arr = {1, 2, 3, 4};
    int n = arr.size();

    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        cout &amp;lt;&amp;lt; &quot;{ &quot;;
        for (int i = 0; i &amp;lt; n; i++) {
            if ((mask &amp;gt;&amp;gt; i) &amp;amp; 1) {
                cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
            }
        }
        cout &amp;lt;&amp;lt; &quot;}\n&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;{ }
{ 1 }
{ 2 }
{ 1 2 }
{ 3 }
...
{ 1 2 3 4 }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4단계 - 부분집합 합이 특정 값인지 찾기&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// {1, 2, 3, 4, 5} 중 합이 7인 부분집합 모두 출력
int main() {
    vector&amp;lt;int&amp;gt; arr = {1, 2, 3, 4, 5};
    int n = arr.size();
    int target = 7;

    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        int sum = 0;
        for (int i = 0; i &amp;lt; n; i++) {
            if ((mask &amp;gt;&amp;gt; i) &amp;amp; 1) {
                sum += arr[i];
            }
        }
        if (sum == target) {
            cout &amp;lt;&amp;lt; &quot;{ &quot;;
            for (int i = 0; i &amp;lt; n; i++) {
                if ((mask &amp;gt;&amp;gt; i) &amp;amp; 1) cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
            }
            cout &amp;lt;&amp;lt; &quot;}\n&quot;;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;&lt;b&gt;5단계 - 방문 체크 (visited 배열 대신 비트 사용)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 일반적인 방법 (bool 배열)
int main() {
    int n = 4;
    vector&amp;lt;bool&amp;gt; visited(n, false);

    visited[1] = true;  // 1번 방문
    visited[3] = true;  // 3번 방문

    cout &amp;lt;&amp;lt; visited[1] &amp;lt;&amp;lt; &quot;\n&quot;; // 1
    cout &amp;lt;&amp;lt; visited[2] &amp;lt;&amp;lt; &quot;\n&quot;; // 0
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 비트마스크로 똑같이 표현
int main() {
    int n = 4;
    int visited = 0; // 0000

    visited |= (1 &amp;lt;&amp;lt; 1);  // 1번 방문 &amp;rarr; 0010
    visited |= (1 &amp;lt;&amp;lt; 3);  // 3번 방문 &amp;rarr; 1010

    cout &amp;lt;&amp;lt; ((visited &amp;gt;&amp;gt; 1) &amp;amp; 1) &amp;lt;&amp;lt; &quot;\n&quot;; // 1번 방문 확인 &amp;rarr; 1
    cout &amp;lt;&amp;lt; ((visited &amp;gt;&amp;gt; 2) &amp;amp; 1) &amp;lt;&amp;lt; &quot;\n&quot;; // 2번 방문 확인 &amp;rarr; 0
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6단계 - 방문 체크 활용 (모든 노드 방문했는지 확인)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 그래프에서 모든 노드를 방문했는지 확인하는 상황
int main() {
    int n = 4;
    int visited = 0;
    int allVisited = (1 &amp;lt;&amp;lt; n) - 1; // 1111 = 15 (모두 방문한 상태)

    // 0, 1, 2, 3번 노드 순서대로 방문
    for (int i = 0; i &amp;lt; n; i++) {
        visited |= (1 &amp;lt;&amp;lt; i);
        cout &amp;lt;&amp;lt; &quot;현재 방문 상태: &quot; &amp;lt;&amp;lt; bitset&amp;lt;4&amp;gt;(visited) &amp;lt;&amp;lt; &quot;\n&quot;;

        if (visited == allVisited) {
            cout &amp;lt;&amp;lt; &quot;모든 노드 방문 완료!\n&quot;;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;현재 방문 상태: 0001
현재 방문 상태: 0011
현재 방문 상태: 0111
현재 방문 상태: 1111
모든 노드 방문 완료!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7단계 - 비트마스크 DP (방문한 노드 집합을 상태로 저장)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 4개 도시를 모두 방문하는 최소 비용 (외판원 순회 간단 버전)
// dist[i][j] = i번 도시에서 j번 도시로 가는 비용
int main() {
    int n = 4;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dist = {
        {0, 10, 15, 20},
        {10, 0, 35, 25},
        {15, 35, 0, 30},
        {20, 25, 30, 0}
    };

    // dp[visited][현재위치] = visited 상태에서 현재위치에 있을 때 최소 비용
    // visited는 비트마스크로 표현 (어떤 도시를 방문했는지)
    int allVisited = (1 &amp;lt;&amp;lt; n) - 1; // 1111
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(1 &amp;lt;&amp;lt; n, vector&amp;lt;int&amp;gt;(n, INT_MAX));

    dp[1][0] = 0; // 0번 도시에서 시작 (0번 방문 = 0001)

    for (int visited = 1; visited &amp;lt; (1 &amp;lt;&amp;lt; n); visited++) {
        for (int cur = 0; cur &amp;lt; n; cur++) {
            if (dp[visited][cur] == INT_MAX) continue;
            // 아직 방문 안 한 도시로 이동
            for (int next = 0; next &amp;lt; n; next++) {
                if ((visited &amp;gt;&amp;gt; next) &amp;amp; 1) continue; // 이미 방문함
                int nextVisited = visited | (1 &amp;lt;&amp;lt; next);
                dp[nextVisited][next] = min(dp[nextVisited][next],
                                           dp[visited][cur] + dist[cur][next]);
            }
        }
    }

    // 모든 도시 방문 후 최소 비용
    int ans = INT_MAX;
    for (int cur = 0; cur &amp;lt; n; cur++) {
        ans = min(ans, dp[allVisited][cur]);
    }
    cout &amp;lt;&amp;lt; &quot;최소 비용: &quot; &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; &quot;\n&quot;; // 60
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름을 정리하면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;1단계: &amp;amp; | ^ &amp;lt;&amp;lt; &amp;gt;&amp;gt;          기본 연산
2단계: 켜기/끄기/확인        비트 조작
3단계: 부분집합 출력         완전탐색 활용
4단계: 부분집합 합 찾기      이번 문제랑 같은 구조
5단계: visited 비트로 표현   bool 배열 대체
6단계: 모든 노드 방문 확인   allVisited 패턴
7단계: 비트마스크 DP         외판원 순회&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7단계까지 직접 돌려보시면 웬만한 비트마스킹 문제는 다 풀 수 있을 거예요.&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비트마스킹 핵심 패턴 6가지&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;cpp&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 1. idx번째 비트 켜기
S |= (1 &amp;lt;&amp;lt; idx)
// 예) S=10000, idx=1 &amp;rarr; 10010

// 2. idx번째 비트 끄기
S &amp;amp;= ~(1 &amp;lt;&amp;lt; idx)
// 예) S=10010, idx=1 &amp;rarr; 10000

// 3. idx번째 비트 반전 (0&amp;rarr;1, 1&amp;rarr;0)
S ^= (1 &amp;lt;&amp;lt; idx)
// 예) S=10010, idx=1 &amp;rarr; 10000
//     S=10000, idx=1 &amp;rarr; 10010

// 4. idx번째 비트 켜져있는지 확인
if (S &amp;amp; (1 &amp;lt;&amp;lt; idx))
// 예) S=10010, idx=1 &amp;rarr; true
//     S=10010, idx=0 &amp;rarr; false

// 5. n개 비트 전부 켜기
(1 &amp;lt;&amp;lt; n) - 1
// 예) n=4 &amp;rarr; 1111 = 15

// 6. 최하위 켜진 비트 찾기 (2의 보수 활용)
S &amp;amp; -S
// 예) S=10010 &amp;rarr; 00010 = 2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;연습 예제 (단계별)&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 6개 + 연습문제 1개로 구성했습니다. 흐름은 이렇습니다:&lt;/p&gt;
&lt;div&gt;&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;예제&lt;/td&gt;
&lt;td&gt;내용&lt;/td&gt;
&lt;td&gt;핵심 패턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;비트 켜기/끄기/확인&lt;/td&gt;
&lt;td&gt;|= &amp;amp;= ~ &amp;amp; (1&amp;lt;&amp;lt;idx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;부분집합 전체 출력&lt;/td&gt;
&lt;td&gt;mask=0 ~ (1&amp;lt;&amp;lt;n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;합이 7인 부분집합&lt;/td&gt;
&lt;td&gt;부분집합 + 조건 필터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;방문 체크&lt;/td&gt;
&lt;td&gt;visited, allVisited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;최하위 켜진 비트&lt;/td&gt;
&lt;td&gt;S &amp;amp; -S (2의 보수)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;XOR 비트 반전&lt;/td&gt;
&lt;td&gt;^= 토글&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;연습&lt;/td&gt;
&lt;td&gt;원소 2개인 부분집합&lt;/td&gt;
&lt;td&gt;__builtin_popcount&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;printBits 함수로 이진수를 눈으로 보면서 확인할 수 있게 했으니, 각 예제 출력을 보면서 비트가 어떻게 바뀌는지 따라가보시면 빠르게 감이 잡힐 거예요.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780477704760&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

// 이진수로 출력해주는 헬퍼 함수 (이해하기 쉽게)
void printBits(int n, int size = 5) {
    for (int i = size - 1; i &amp;gt;= 0; i--) {
        cout &amp;lt;&amp;lt; ((n &amp;gt;&amp;gt; i) &amp;amp; 1);
    }
    cout &amp;lt;&amp;lt; &quot; (&quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;)&quot;;
}

// =============================================
// 예제 1. 비트 켜기 / 끄기 / 확인
// =============================================
void example1() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 1. 비트 켜기 / 끄기 / 확인 =====\n&quot;;
    int S = 0b10000; // 16

    cout &amp;lt;&amp;lt; &quot;초기값 S = &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;

    // 1번째 비트 켜기
    S |= (1 &amp;lt;&amp;lt; 1);
    cout &amp;lt;&amp;lt; &quot;1번째 비트 켜기 후: &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;

    // 3번째 비트 켜기
    S |= (1 &amp;lt;&amp;lt; 3);
    cout &amp;lt;&amp;lt; &quot;3번째 비트 켜기 후: &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;

    // 1번째 비트 끄기
    S &amp;amp;= ~(1 &amp;lt;&amp;lt; 1);
    cout &amp;lt;&amp;lt; &quot;1번째 비트 끄기 후: &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;

    // 3번째 비트 켜져있는지 확인
    if (S &amp;amp; (1 &amp;lt;&amp;lt; 3)) cout &amp;lt;&amp;lt; &quot;3번째 비트 켜져 있음\n&quot;;
    else               cout &amp;lt;&amp;lt; &quot;3번째 비트 꺼져 있음\n&quot;;

    // 1번째 비트 켜져있는지 확인
    if (S &amp;amp; (1 &amp;lt;&amp;lt; 1)) cout &amp;lt;&amp;lt; &quot;1번째 비트 켜져 있음\n&quot;;
    else               cout &amp;lt;&amp;lt; &quot;1번째 비트 꺼져 있음\n&quot;;

    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

// =============================================
// 예제 2. 부분집합 전체 출력
// =============================================
void example2() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 2. 부분집합 전체 출력 =====\n&quot;;
    vector&amp;lt;string&amp;gt; items = {&quot;사과&quot;, &quot;딸기&quot;, &quot;포도&quot;, &quot;배&quot;};
    int n = items.size();

    // 0b0000 ~ 0b1111 (0 ~ 15) 순회 = 모든 부분집합
    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        cout &amp;lt;&amp;lt; &quot;mask=&quot;; printBits(mask, 4); cout &amp;lt;&amp;lt; &quot; &amp;rarr; { &quot;;
        for (int i = 0; i &amp;lt; n; i++) {
            if (mask &amp;amp; (1 &amp;lt;&amp;lt; i)) {
                cout &amp;lt;&amp;lt; items[i] &amp;lt;&amp;lt; &quot; &quot;;
            }
        }
        cout &amp;lt;&amp;lt; &quot;}\n&quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

// =============================================
// 예제 3. 부분집합 합이 target인 경우 찾기
// =============================================
void example3() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 3. 합이 7인 부분집합 찾기 =====\n&quot;;
    vector&amp;lt;int&amp;gt; arr = {1, 2, 3, 4, 5};
    int n = arr.size();
    int target = 7;

    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        int sum = 0;
        for (int i = 0; i &amp;lt; n; i++) {
            if (mask &amp;amp; (1 &amp;lt;&amp;lt; i)) sum += arr[i];
        }
        if (sum == target) {
            cout &amp;lt;&amp;lt; &quot;{ &quot;;
            for (int i = 0; i &amp;lt; n; i++) {
                if (mask &amp;amp; (1 &amp;lt;&amp;lt; i)) cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
            }
            cout &amp;lt;&amp;lt; &quot;} = &quot; &amp;lt;&amp;lt; target &amp;lt;&amp;lt; &quot;\n&quot;;
        }
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

// =============================================
// 예제 4. 비트로 방문 체크 (visited 대신)
// =============================================
void example4() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 4. 비트로 방문 체크 =====\n&quot;;
    int n = 5;
    int visited = 0;
    int allVisited = (1 &amp;lt;&amp;lt; n) - 1; // 11111

    cout &amp;lt;&amp;lt; &quot;목표 상태(전부 방문): &quot;; printBits(allVisited); cout &amp;lt;&amp;lt; &quot;\n\n&quot;;

    // 0, 2, 4번 노드 방문
    vector&amp;lt;int&amp;gt; order = {0, 2, 4};
    for (int node : order) {
        visited |= (1 &amp;lt;&amp;lt; node);
        cout &amp;lt;&amp;lt; node &amp;lt;&amp;lt; &quot;번 노드 방문 후: &quot;; printBits(visited); cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }

    cout &amp;lt;&amp;lt; &quot;\n모든 노드 방문했나? &quot;;
    cout &amp;lt;&amp;lt; (visited == allVisited ? &quot;YES&quot; : &quot;NO&quot;) &amp;lt;&amp;lt; &quot;\n&quot;;

    // 나머지도 방문
    visited |= (1 &amp;lt;&amp;lt; 1);
    visited |= (1 &amp;lt;&amp;lt; 3);
    cout &amp;lt;&amp;lt; &quot;나머지 방문 후: &quot;; printBits(visited); cout &amp;lt;&amp;lt; &quot;\n&quot;;
    cout &amp;lt;&amp;lt; &quot;모든 노드 방문했나? &quot;;
    cout &amp;lt;&amp;lt; (visited == allVisited ? &quot;YES&quot; : &quot;NO&quot;) &amp;lt;&amp;lt; &quot;\n\n&quot;;
}

// =============================================
// 예제 5. 최하위 켜진 비트 찾기 (2의 보수 활용)
// =============================================
void example5() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 5. 최하위 켜진 비트 찾기 =====\n&quot;;
    vector&amp;lt;int&amp;gt; tests = {0b10010, 0b10100, 0b11000};

    for (int S : tests) {
        int lowest = S &amp;amp; -S;
        cout &amp;lt;&amp;lt; &quot;S = &quot;; printBits(S);
        cout &amp;lt;&amp;lt; &quot;  &amp;rarr;  최하위 켜진 비트 = &quot;; printBits(lowest);
        cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

// =============================================
// 예제 6. XOR로 비트 반전
// =============================================
void example6() {
    cout &amp;lt;&amp;lt; &quot;===== 예제 6. XOR로 비트 반전 =====\n&quot;;
    int S = 0b10000;
    cout &amp;lt;&amp;lt; &quot;초기값: &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;

    // 토글처럼 사용 (켜져있으면 끄고, 꺼져있으면 켜고)
    for (int i = 0; i &amp;lt; 3; i++) {
        S ^= (1 &amp;lt;&amp;lt; 1); // 1번째 비트 반전
        cout &amp;lt;&amp;lt; &quot;1번째 비트 반전: &quot;; printBits(S); cout &amp;lt;&amp;lt; &quot;\n&quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;
}

// =============================================
// 연습 문제: 아래 TODO를 직접 채워보세요!
// =============================================
void practice() {
    cout &amp;lt;&amp;lt; &quot;===== 연습 문제 =====\n&quot;;
    cout &amp;lt;&amp;lt; &quot;{1, 2, 3, 4, 5} 중 원소 개수가 정확히 2개인 부분집합을 출력하세요.\n&quot;;

    vector&amp;lt;int&amp;gt; arr = {1, 2, 3, 4, 5};
    int n = arr.size();

    for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++) {
        // TODO: mask에서 켜진 비트 수를 세서 2개인 경우만 출력
        int cnt = __builtin_popcount(mask); // 켜진 비트 수 세기
        if (cnt == 2) {
            cout &amp;lt;&amp;lt; &quot;{ &quot;;
            for (int i = 0; i &amp;lt; n; i++) {
                if (mask &amp;amp; (1 &amp;lt;&amp;lt; i)) cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
            }
            cout &amp;lt;&amp;lt; &quot;}\n&quot;;
        }
    }
}

int main() {
    example1();
    example2();
    example3();
    example4();
    example5();
    example6();
    practice();
    return 0;
}&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Extra&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비트마스킹 핵심 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;n개 원소 집합의 모든 부분집합 순회&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;for (int mask = 0; mask &amp;lt; (1 &amp;lt;&amp;lt; n); mask++)
// n=4이면 0000 ~ 1111, 총 16가지&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;idx번째 원소 확인 (켜져있는지)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;if ((mask &amp;gt;&amp;gt; idx) &amp;amp; 1)
// mask=0101, idx=2 &amp;rarr; (0101 &amp;gt;&amp;gt; 2) &amp;amp; 1 = 0001 &amp;amp; 1 = 1 &amp;rarr; 존재
// mask=0101, idx=1 &amp;rarr; (0101 &amp;gt;&amp;gt; 1) &amp;amp; 1 = 0010 &amp;amp; 1 = 0 &amp;rarr; 없음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;idx번째 원소 추가 (비트 켜기)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;django&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;mask |= (1 &amp;lt;&amp;lt; idx)
// mask=0101, idx=1 &amp;rarr; 0101 | 0010 = 0111&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;idx번째 원소 삭제 (비트 끄기)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;django&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;mask &amp;amp;= ~(1 &amp;lt;&amp;lt; idx)
// mask=0101, idx=2 &amp;rarr; 0101 &amp;amp; ~0100 = 0101 &amp;amp; 1011 = 0001&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;idx번째 원소 반전 (0&amp;rarr;1, 1&amp;rarr;0)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;django&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;mask ^= (1 &amp;lt;&amp;lt; idx)
// mask=0101, idx=1 &amp;rarr; 0101 ^ 0010 = 0111 (켜짐)
// mask=0111, idx=1 &amp;rarr; 0111 ^ 0010 = 0101 (꺼짐)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;n개 비트 전부 켜기 (모든 원소 포함)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;(1 &amp;lt;&amp;lt; n) - 1
// n=4 &amp;rarr; 10000 - 1 = 01111&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최하위 켜진 비트 찾기&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;scss&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;mask &amp;amp; -mask
// mask=10010 &amp;rarr; 00010 (2의 보수 활용)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>프로그래머스/코딩테스트</category>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2098</guid>
      <comments>https://junworld.tistory.com/2098#entry2098comment</comments>
      <pubDate>Wed, 3 Jun 2026 18:08:31 +0900</pubDate>
    </item>
    <item>
      <title>추천시스템 RecommenderMetrics.py 둘러보기</title>
      <link>https://junworld.tistory.com/2096</link>
      <description>&lt;pre id=&quot;code_1780380237661&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import itertools
from surprise import accuracy
from collections import defaultdict

class RecommenderMetrics:

    # ─────────────────────────────
    # MAE: 평균 절대 오차
    # 예측 평점과 실제 평점의 절댓값 평균
    # ─────────────────────────────
    def MAE(predictions):
        return accuracy.mae(predictions, verbose=False)

    # ─────────────────────────────
    # RMSE: 평균 제곱근 오차
    # 예측 평점과 실제 평점의 제곱 평균의 루트
    # ─────────────────────────────
    def RMSE(predictions):
        return accuracy.rmse(predictions, verbose=False)

    # ─────────────────────────────
    # GetTopN: 사용자별 상위 N개 추천 목록 생성
    # n: 추천 개수 (기본 10개)
    # minimumRating: 최소 예측 평점 기준 (기본 4.0)
    # ─────────────────────────────
    def GetTopN(predictions, n=10, minimumRating=4.0):
        topN = defaultdict(list)

        for userID, movieID, actualRating, estimatedRating, _ in predictions:
            # 예측 평점이 최소 기준 이상인 것만 추가
            if (estimatedRating &amp;gt;= minimumRating):
                topN[int(userID)].append((int(movieID), estimatedRating))

        for userID, ratings in topN.items():
            # 예측 평점 높은 순으로 정렬
            ratings.sort(key=lambda x: x[1], reverse=True)
            # 상위 N개만 유지
            topN[int(userID)] = ratings[:n]

        return topN

    # ─────────────────────────────
    # HitRate: 적중률
    # Leave-One-Out 방식으로 숨긴 항목이
    # 추천 목록에 있으면 적중
    # ─────────────────────────────
    def HitRate(topNPredicted, leftOutPredictions):
        hits = 0
        total = 0

        for leftOut in leftOutPredictions:
            userID = leftOut[0]
            leftOutMovieID = leftOut[1]

            # 숨긴 영화가 추천 목록에 있는지 확인
            hit = False
            for movieID, predictedRating in topNPredicted[int(userID)]:
                if (int(leftOutMovieID) == int(movieID)):
                    hit = True
                    break

            if (hit):
                hits += 1
            total += 1

        # 적중률 = 적중 수 / 전체 사용자 수
        return hits / total

    # ─────────────────────────────
    # CumulativeHitRate: 누적 적중률
    # 실제 평점이 기준(ratingCutoff) 이상인 것만 적중으로 인정
    # 사용자가 실제로 좋아하는 영화를 추천했는지 측정
    # ─────────────────────────────
    def CumulativeHitRate(topNPredicted, leftOutPredictions, ratingCutoff=0):
        hits = 0
        total = 0

        for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:
            # 실제 평점이 기준 이상인 것만 측정
            if (actualRating &amp;gt;= ratingCutoff):
                hit = False
                for movieID, predictedRating in topNPredicted[int(userID)]:
                    if (int(leftOutMovieID) == movieID):
                        hit = True
                        break
                if (hit):
                    hits += 1
                total += 1

        return hits / total

    # ─────────────────────────────
    # RatingHitRate: 평점별 적중률
    # 평점 구간별로 적중률을 나눠서 출력
    # 어떤 평점대의 영화를 잘 추천하는지 분석
    # ─────────────────────────────
    def RatingHitRate(topNPredicted, leftOutPredictions):
        hits = defaultdict(float)
        total = defaultdict(float)

        for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:
            hit = False
            for movieID, predictedRating in topNPredicted[int(userID)]:
                if (int(leftOutMovieID) == movieID):
                    hit = True
                    break
            if (hit):
                # 실제 평점별로 적중 수 카운트
                hits[actualRating] += 1
            total[actualRating] += 1

        # 평점별 적중률 출력
        # 이상적: 5점짜리 적중률이 가장 높아야 함
        for rating in sorted(hits.keys()):
            print(rating, hits[rating] / total[rating])

    # ─────────────────────────────
    # AverageReciprocalHitRank: ARHR
    # 적중 위치가 상위일수록 높은 점수
    # 공식: &amp;Sigma;(1/rank) / 전체 사용자 수
    # ─────────────────────────────
    def AverageReciprocalHitRank(topNPredicted, leftOutPredictions):
        summation = 0
        total = 0

        for userID, leftOutMovieID, actualRating, estimatedRating, _ in leftOutPredictions:
            hitRank = 0
            rank = 0
            for movieID, predictedRating in topNPredicted[int(userID)]:
                rank = rank + 1
                if (int(leftOutMovieID) == movieID):
                    hitRank = rank  # 적중한 순위 저장
                    break

            if (hitRank &amp;gt; 0):
                # 1번 적중 &amp;rarr; 1/1 = 1.0
                # 5번 적중 &amp;rarr; 1/5 = 0.2
                summation += 1.0 / hitRank
            total += 1

        # ARHR = 전체 1/rank 합계 / 전체 사용자 수
        return summation / total

    # ─────────────────────────────
    # UserCoverage: 제공률
    # 최소 하나 이상의 좋은 추천을 받은 사용자 비율
    # ratingThreshold: 좋은 추천 기준 평점
    # ─────────────────────────────
    def UserCoverage(topNPredicted, numUsers, ratingThreshold=0):
        hits = 0
        for userID in topNPredicted.keys():
            hit = False
            for movieID, predictedRating in topNPredicted[userID]:
                # 기준 평점 이상인 추천이 하나라도 있으면 적중
                if (predictedRating &amp;gt;= ratingThreshold):
                    hit = True
                    break
            if (hit):
                hits += 1

        # 제공률 = 좋은 추천 받은 사용자 수 / 전체 사용자 수
        return hits / numUsers

    # ─────────────────────────────
    # Diversity: 다양성
    # 추천 목록 내 항목들의 평균 유사도를 1에서 뺀 값
    # 공식: Diversity = 1 - S (S = 평균 유사도)
    # ─────────────────────────────
    def Diversity(topNPredicted, simsAlgo):
        n = 0
        total = 0
        # 유사도 행렬 계산
        simsMatrix = simsAlgo.compute_similarities()

        for userID in topNPredicted.keys():
            # 추천 목록 내 모든 영화 쌍 조합
            pairs = itertools.combinations(topNPredicted[userID], 2)
            for pair in pairs:
                movie1 = pair[0][0]
                movie2 = pair[1][0]
                # 내부 ID로 변환
                innerID1 = simsAlgo.trainset.to_inner_iid(str(movie1))
                innerID2 = simsAlgo.trainset.to_inner_iid(str(movie2))
                # 두 영화 간 유사도
                similarity = simsMatrix[innerID1][innerID2]
                total += similarity
                n += 1

        # 평균 유사도
        S = total / n
        # 다양성 = 1 - 평균 유사도
        return (1 - S)

    # ─────────────────────────────
    # Novelty: 참신성
    # 추천 항목의 평균 인기 순위
    # 순위가 높을수록(비인기) 참신성 높음
    # ─────────────────────────────
    def Novelty(topNPredicted, rankings):
        n = 0
        total = 0
        for userID in topNPredicted.keys():
            for rating in topNPredicted[userID]:
                movieID = rating[0]
                # 영화의 인기 순위 가져오기
                # 순위가 높은 숫자 = 비인기 영화 = 참신성 높음
                rank = rankings[movieID]
                total += rank
                n += 1

        # 참신성 = 추천 항목들의 평균 인기 순위
        return total / n&lt;/code&gt;&lt;/pre&gt;</description>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2096</guid>
      <comments>https://junworld.tistory.com/2096#entry2096comment</comments>
      <pubDate>Tue, 2 Jun 2026 15:04:05 +0900</pubDate>
    </item>
    <item>
      <title>추천 시스템 핵심 매트릭 총정리</title>
      <link>https://junworld.tistory.com/2095</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 시스템 핵심 매트릭 총정리&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. RMSE (평균 제곱근 오차)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;넷플릭스 프라이즈 평가 지표
평점 예측 정확도 측정

BUT
정확도가 전부가 아님!
사용자는 평점 예측보다
새로운 것을 추천받기를 원함&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. ARHR (평균 상호 적중률)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;공식: &amp;Sigma;(1/rank) / Users

추천 순위 목록의 품질 측정
&amp;rarr; 상위에 좋은 영화를 추천할수록 높은 점수
&amp;rarr; RMSE보다 실용적인 지표&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 참신성 (Novelty)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;추천 항목의 인기 척도 측정

인기 항목 추천 &amp;rarr; 신뢰 &amp;uarr; 참신성 &amp;darr;
비인기 항목 추천 &amp;rarr; 참신성 &amp;uarr; 신뢰 &amp;darr;

롱테일 전략과 연결
&amp;rarr; 비인기 항목도 특이한 취향의
  사용자에게는 사회적 가치 있음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 다양성 (Diversity)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;공식: Diversity = 1 - 평균유사도(S)

추천 목록 내 항목들이 얼마나 다양한지 측정

⚠️ 주의
다양성 높음 &amp;ne; 좋은 추천
&amp;rarr; 아무거나 추천하면 다양성 높아짐
&amp;rarr; 스타워즈만 추천 &amp;rarr; 다양성 낮음
&amp;rarr; 아무거나 추천  &amp;rarr; 다양성 높음 ❌&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. A/B 테스트 (가장 중요!)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;A그룹 &amp;rarr; 기존 알고리즘
B그룹 &amp;rarr; 새로운 알고리즘
&amp;rarr; 실제 사용자 반응 비교

모든 오프라인 지표보다 중요!
실제 사용자가 어떻게 반응하는지가
궁극적인 평가 지표&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최종 정리&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;매트릭&lt;/td&gt;
&lt;td&gt;측정 대상&lt;/td&gt;
&lt;td&gt;중요도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMSE&lt;/td&gt;
&lt;td&gt;평점 예측 정확도&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARHR&lt;/td&gt;
&lt;td&gt;추천 순위 품질&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참신성&lt;/td&gt;
&lt;td&gt;추천 항목 인기도&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다양성&lt;/td&gt;
&lt;td&gt;추천 항목 다양함&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A/B 테스트&lt;/td&gt;
&lt;td&gt;실제 사용자 반응&lt;/td&gt;
&lt;td&gt;최고 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프라인 지표는 개발 중 참고용 &lt;b&gt;실제 사용자 반응(A/B 테스트)&lt;/b&gt; 이 추천 시스템의 궁극적인 평가 지표!&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2095</guid>
      <comments>https://junworld.tistory.com/2095#entry2095comment</comments>
      <pubDate>Tue, 2 Jun 2026 14:07:41 +0900</pubDate>
    </item>
    <item>
      <title>추천시스템 이탈 응답성 및 A/B테스트</title>
      <link>https://junworld.tistory.com/2094</link>
      <description>&lt;div style=&quot;background-color: #fdfdfc; color: #0b0b0b; text-align: start;&quot;&gt;
&lt;div data-test-render-count=&quot;2&quot;&gt;
&lt;div style=&quot;color: #373734;&quot;&gt;
&lt;div style=&quot;color: #373734;&quot;&gt;&lt;span style=&quot;color: #121212; font-size: 1.44em; letter-spacing: -1px;&quot;&gt;추가 측정 지표 정리&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fdfdfc; color: #0b0b0b; text-align: start;&quot;&gt;
&lt;div data-test-render-count=&quot;1&quot;&gt;
&lt;div data-is-streaming=&quot;false&quot;&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;1. 이탈 (Churn)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;추천 목록이 얼마나 자주 바뀌는가

사용자가 새 영화 평가
&amp;rarr; 추천 목록 변화 많음 &amp;rarr; 이탈 높음
&amp;rarr; 추천 목록 변화 없음 &amp;rarr; 이탈 낮음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️ 주의&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang-repl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;이탈 높음 &amp;ne; 좋은 추천
&amp;rarr; 무작위 추천하면 이탈 높아짐
&amp;rarr; 다양성, 참신성처럼 균형이 중요!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;2. 민감성 (Responsiveness)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;사용자의 새로운 행위가
추천 시스템에 얼마나 빠르게 반영되는가

즉각 반영 &amp;rarr; 민감성 높음
다음날 반영 &amp;rarr; 민감성 낮음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;&lt;b&gt;트레이드오프&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang-repl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;민감성 높음 &amp;rarr; 시스템 복잡 &amp;rarr; 유지비용 증가
민감성 낮음 &amp;rarr; 시스템 단순 &amp;rarr; 반응 느림

민감성 vs 간소성 균형 필요!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;3. 전체 지표 요약&lt;/h4&gt;
&lt;div&gt;&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;지표&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;td&gt;높으면&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAE/RMSE&lt;/td&gt;
&lt;td&gt;평점 예측 정확도&lt;/td&gt;
&lt;td&gt;오차 큼 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적중률&lt;/td&gt;
&lt;td&gt;추천 순위 정확도&lt;/td&gt;
&lt;td&gt;좋은 추천 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제공률&lt;/td&gt;
&lt;td&gt;추천 가능 비율&lt;/td&gt;
&lt;td&gt;많은 항목 추천 가능 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다양성&lt;/td&gt;
&lt;td&gt;추천 항목 다양함&lt;/td&gt;
&lt;td&gt;아무거나 추천 위험 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참신성&lt;/td&gt;
&lt;td&gt;비대중적 추천&lt;/td&gt;
&lt;td&gt;신뢰 낮아질 위험 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이탈&lt;/td&gt;
&lt;td&gt;추천 목록 변화 빈도&lt;/td&gt;
&lt;td&gt;무작위 추천 위험 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;민감성&lt;/td&gt;
&lt;td&gt;새 행위 반영 속도&lt;/td&gt;
&lt;td&gt;시스템 복잡 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;4. 최종 평가: A/B 테스트&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;오프라인 지표 &amp;rarr; 개발 중 참고용
온라인 A/B 테스트 &amp;rarr; 최종 평가 지표

A그룹: 알고리즘 A 추천
B그룹: 알고리즘 B 추천
&amp;rarr; 실제 구매/클릭/반응 비교
&amp;rarr; 더 좋은 알고리즘 선택&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;5. 대리 문제 (Proxy Problem)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;유튜브 연구 결과:
평점 예측 정확도 높음
BUT 실제 추천은 별로

&amp;rarr; 정확성 &amp;ne; 좋은 추천
&amp;rarr; 넷플릭스도 같은 결론
   (백만달러 대회 정확성 지표 실제로 안씀)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대리 문제 (Proxy Problem)&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개념&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;측정하기 쉬운 지표(대리 지표)가
실제 목표와 다를 수 있는 문제&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쉬운 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;makefile&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;목표: 좋은 선생님 찾기
대리 지표: 시험 점수

시험 점수만 올리는 선생님
&amp;rarr; 대리 지표 높음 ✅
&amp;rarr; 실제 좋은 선생님? ❌&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 시스템에서&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;목표: 사용자가 좋아할 영화 추천
대리 지표: RMSE (평점 예측 정확도)

RMSE 낮음 (평점 예측 정확)
&amp;rarr; 대리 지표 좋음 ✅
&amp;rarr; 실제 좋은 추천? ❌ (아닐 수 있음)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유튜브 사례&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;1c&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;평점 예측 정확도 높은 알고리즘 개발
&amp;rarr; RMSE 좋음
&amp;rarr; 실제 영상 추천은 별로
&amp;rarr; 사용자 클릭 안함

&quot;대리 문제를 골라내는 것은
과학보다 예술의 영역이다&quot; - 유튜브&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대리 지표(RMSE)를 최적화한다고 실제 목표(좋은 추천)가 달성되는 건 아니에요! 그래서 &lt;b&gt;A/B 테스트로 실제 사용자 반응&lt;/b&gt;을 확인하는 것이 중요해요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;6. 사용자 직접 피드백&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;makefile&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;장점: 직접적인 품질 평가 가능
단점:
&amp;rarr; 항목 품질 vs 추천 품질 혼동
&amp;rarr; 평가 데이터 부족
&amp;rarr; 해석 어려움

결론: A/B 테스트가 더 신뢰할 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #121212;&quot; data-ke-size=&quot;size23&quot;&gt;최종 결론&lt;/h3&gt;
&lt;blockquote style=&quot;color: #373734;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오프라인 지표&lt;/b&gt;는 개발 중 참고용&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;온라인 A/B 테스트&lt;/b&gt;가 궁극적인 평가 지표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추천 시스템은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;과학이 아닌 예술&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 지표의 균형을 찾는 것이 핵심!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&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;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A/B 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사용자를 두 그룹으로 나눠서 &lt;b&gt;어떤 것이 더 효과적인지&lt;/b&gt; 비교하는 실험이에요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;전체 사용자
      &amp;darr;
  ┌───┴───┐
  A그룹   B그룹
  (기존)  (새로운)
  알고리즘 알고리즘
      &amp;darr;       &amp;darr;
  반응 측정  반응 측정
      &amp;darr;
  더 좋은 것 선택&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 시스템 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;ldif&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;A그룹 (100명): 기존 추천 알고리즘
B그룹 (100명): 새로운 추천 알고리즘

측정 항목:
- 클릭률
- 구매율
- 체류 시간

결과:
A그룹 구매율: 5%
B그룹 구매율: 8% ✅

&amp;rarr; B 알고리즘 채택!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실생활 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dns&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;넷플릭스 &amp;rarr; 썸네일 A vs 썸네일 B
유튜브   &amp;rarr; 추천 알고리즘 A vs B
아마존   &amp;rarr; 상품 배치 A vs B&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오프라인 지표 vs A/B 테스트&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;오프라인 지표 (RMSE, 적중률 등)
&amp;rarr; 실제 데이터로 계산
&amp;rarr; 빠르고 저렴
&amp;rarr; 실제 사용자 반응 모름

A/B 테스트
&amp;rarr; 실제 사용자로 실험
&amp;rarr; 느리고 비쌈
&amp;rarr; 실제 사용자 반응 측정 가능 ✅&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A/B 테스트는 &lt;b&gt;실제 사용자 반응&lt;/b&gt;을 측정하는 가장 신뢰할 수 있는 평가 방법이에요! 오프라인 지표가 좋아도 A/B 테스트에서 실패하면 의미없어요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A/B 테스트가 느리고 비싼 이유&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;느린 이유&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;충분한 데이터 수집 필요

사용자 100명으로 테스트
&amp;rarr; 데이터 부족 &amp;rarr; 신뢰도 낮음

최소 수천 ~ 수만 명 필요
&amp;rarr; 그만큼 시간이 걸림

또한
사용자 행동 데이터 수집
&amp;rarr; 클릭, 구매, 체류시간 등
&amp;rarr; 충분한 기간 필요 (보통 수주 ~ 수개월)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비싼 이유&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;1. 인프라 비용
&amp;rarr; 두 가지 알고리즘 동시 운영
&amp;rarr; 서버 비용 2배

2. 개발 비용
&amp;rarr; A/B 테스트 시스템 구축
&amp;rarr; 데이터 수집 파이프라인 구축

3. 기회비용
&amp;rarr; B그룹에 나쁜 알고리즘 적용 시
&amp;rarr; 그 기간 동안 손실 발생

4. 분석 비용
&amp;rarr; 결과 데이터 분석 인력 필요&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;오프라인 지표로 먼저 걸러내고&lt;/b&gt; 가능성 있는 알고리즘만 &lt;b&gt;A/B 테스트로 최종 검증&lt;/b&gt;하는 방식을 써요!&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2094</guid>
      <comments>https://junworld.tistory.com/2094#entry2094comment</comments>
      <pubDate>Tue, 2 Jun 2026 13:16:41 +0900</pubDate>
    </item>
    <item>
      <title>추천시스템 품질 측정 요소</title>
      <link>https://junworld.tistory.com/2093</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 시스템 품질 측정 요소&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 제공률 (Coverage)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;fix&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;추천 시스템이 추천 가능한 항목의 비율

제공률 = 추천 가능한 항목 수 / 전체 항목 수&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정확성 vs 제공률 트레이드오프&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang-repl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;정확성 &amp;uarr; &amp;rarr; 제공률 &amp;darr;
제공률 &amp;uarr; &amp;rarr; 정확성 &amp;darr;

균형점을 찾는 것이 중요!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;무비렌즈 데이터로 학습
&amp;rarr; IMDB 전체 영화에 적용 시
&amp;rarr; 평점 없는 영화 추천 불가
&amp;rarr; 제공률 낮음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 다양성 (Diversity)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공식&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;ini&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;S = 추천 목록 내 항목들의 평균 유사도
Diversity = 1 - S&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;추천 목록: [해리포터1, 해리포터2, 해리포터3]
&amp;rarr; 유사도 S = 0.9 (매우 비슷)
&amp;rarr; Diversity = 1 - 0.9 = 0.1 (다양성 낮음)

추천 목록: [해리포터, 액션영화, 다큐멘터리]
&amp;rarr; 유사도 S = 0.2
&amp;rarr; Diversity = 1 - 0.2 = 0.8 (다양성 높음)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚠️ 주의&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang-repl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;다양성 높음 &amp;ne; 좋은 추천
&amp;rarr; 아무거나 추천하면 다양성은 높아짐
&amp;rarr; 다양성이 너무 높으면 오히려 나쁜 추천!&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #fdfdfc; color: #0b0b0b; text-align: start;&quot;&gt;
&lt;div data-test-render-count=&quot;1&quot;&gt;
&lt;div data-is-streaming=&quot;false&quot;&gt;
&lt;h3 style=&quot;color: #121212;&quot; data-ke-size=&quot;size23&quot;&gt;유사성 측정 방법&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;1. 코사인 유사도 (Cosine Similarity)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;두 벡터 사이의 각도로 유사성 측정

cos(&amp;theta;) = (A&amp;middot;B) / (|A| &amp;times; |B|)

범위: -1 ~ 1
1  = 완전히 같음
0  = 관계 없음
-1 = 완전히 반대&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;영화A 장르: [액션=1, 로맨스=0, SF=1]
영화B 장르: [액션=1, 로맨스=0, SF=1]
&amp;rarr; cos(&amp;theta;) = 1.0 (완전히 같음)

영화A: [액션=1, 로맨스=0, SF=1]
영화C: [액션=0, 로맨스=1, SF=0]
&amp;rarr; cos(&amp;theta;) = 0.0 (관계 없음)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;2. 피어슨 상관계수 (Pearson Correlation)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;두 변수의 선형 관계 측정

r = &amp;Sigma;(A-평균A)(B-평균B) / &amp;radic;(&amp;Sigma;(A-평균A)&amp;sup2; &amp;times; &amp;Sigma;(B-평균B)&amp;sup2;)

범위: -1 ~ 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;사용자A 평점: [5, 4, 3, 2]
사용자B 평점: [5, 4, 3, 2]
&amp;rarr; r = 1.0 (완전히 같은 취향)

사용자A 평점: [5, 4, 3, 2]
사용자C 평점: [1, 2, 3, 4]
&amp;rarr; r = -1.0 (완전히 반대 취향)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #121212;&quot; data-ke-size=&quot;size20&quot;&gt;3. 유클리드 거리 (Euclidean Distance)&lt;/h4&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;두 점 사이의 직선 거리

d = &amp;radic;((A1-B1)&amp;sup2; + (A2-B2)&amp;sup2; + ...)

거리가 가까울수록 유사성 높음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #000000;&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;영화A: [액션=5, 로맨스=1]
영화B: [액션=4, 로맨스=2]

d = &amp;radic;((5-4)&amp;sup2; + (1-2)&amp;sup2;)
  = &amp;radic;(1 + 1)
  = &amp;radic;2 &amp;asymp; 1.41 (가까움 = 유사함)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #121212;&quot; data-ke-size=&quot;size23&quot;&gt;추천 시스템에서 주로 사용&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;방법&lt;/td&gt;
&lt;td&gt;주 사용 처&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코사인 유사도&lt;/td&gt;
&lt;td&gt;아이템 기반 추천 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;피어슨 상관계수&lt;/td&gt;
&lt;td&gt;사용자 기반 추천 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유클리드 거리&lt;/td&gt;
&lt;td&gt;간단한 유사도 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 style=&quot;color: #121212;&quot; data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;blockquote style=&quot;color: #373734;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추천 시스템에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다양성(Diversity)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 측정할 때 추천 목록 내 항목들끼리&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;코사인 유사도&lt;/b&gt;로 유사성을 구한 뒤&lt;span&gt;&amp;nbsp;&lt;/span&gt;Diversity = 1 - 평균유사도&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 계산해요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 참신성 (Novelty)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;추천 데이터가 얼마나 비대중적인가
&amp;rarr; 사용자가 모르는 새로운 것을 추천하는 정도&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참신성 vs 신뢰성 트레이드오프&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;erlang-repl&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;참신성 높음 &amp;rarr; 낯선 추천 &amp;rarr; 사용자 신뢰 낮음
참신성 낮음 &amp;rarr; 대중적 추천 &amp;rarr; 사용자 신뢰 높음

균형점을 찾는 것이 중요!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 롱테일 (Long Tail) 전략&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;1c&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;판매량
&amp;uarr;
|█
|██
|████
|████████______________ &amp;rarr; 롱테일
+------------------------&amp;rarr; 전체 상품
  인기상품  틈새상품&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;대부분의 판매 = 소수의 인기 상품
롱테일 = 틈새 수요를 가진 비인기 상품들

추천 시스템의 목표:
인기 상품(신뢰) + 롱테일 상품(참신성) 균형!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 사용자 신뢰 (User Trust)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;익숙한 추천 &amp;rarr; 신뢰 &amp;uarr;
낯선 추천만  &amp;rarr; 신뢰 &amp;darr;

이상적인 추천:
익숙한 데이터(신뢰) + 새로운 데이터(참신성) 균형&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 요약&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;요소&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;td&gt;높으면&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제공률&lt;/td&gt;
&lt;td&gt;추천 가능 비율&lt;/td&gt;
&lt;td&gt;많은 항목 추천 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다양성&lt;/td&gt;
&lt;td&gt;추천 항목 간 다양함&lt;/td&gt;
&lt;td&gt;아무거나 추천 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참신성&lt;/td&gt;
&lt;td&gt;비대중적 추천 정도&lt;/td&gt;
&lt;td&gt;신뢰 낮아질 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&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;/blockquote&gt;</description>
      <author>junl</author>
      <guid isPermaLink="true">https://junworld.tistory.com/2093</guid>
      <comments>https://junworld.tistory.com/2093#entry2093comment</comments>
      <pubDate>Tue, 2 Jun 2026 13:13:34 +0900</pubDate>
    </item>
  </channel>
</rss>