<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>g1fted_13.log</title>
        <link>https://velog.io/</link>
        <description>어제보다, 더</description>
        <lastBuildDate>Fri, 27 Jun 2025 15:08:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>g1fted_13.log</title>
            <url>https://velog.velcdn.com/images/g1fted_13/profile/6ef4f26b-0eab-4c39-9354-08bdbbe4ce25/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. g1fted_13.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/g1fted_13" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[BOJ 1504 - 특정한 최단 경로(C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-1504-%ED%8A%B9%EC%A0%95%ED%95%9C-%EC%B5%9C%EB%8B%A8-%EA%B2%BD%EB%A1%9CC</link>
            <guid>https://velog.io/@g1fted_13/BOJ-1504-%ED%8A%B9%EC%A0%95%ED%95%9C-%EC%B5%9C%EB%8B%A8-%EA%B2%BD%EB%A1%9CC</guid>
            <pubDate>Fri, 27 Jun 2025 15:08:24 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1504">https://www.acmicpc.net/problem/1504</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/ead732ac-5755-460e-8029-ca8e4e0ec789/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-06-27">문제를 푼 날짜: 2025. 06. 27</h3>
<h3 id="graphs-shortest_path-dijkstra">#graphs #shortest_path #dijkstra</h3>
<h2 id="내-풀이c">내 풀이(C++)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;

using namespace std;
const int INF = 1000000000 / 3;

// N: 정점의 개수, E: 간선의 개수
int N, E;

// (to, weight)
vector&lt;vector&lt;pair&lt;int, int&gt;&gt;&gt; graph;

// Return: depart부터 arrival 까지 최단경로
int dijkstra(int depart, int arrival);

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin &gt;&gt; N &gt;&gt; E;
    graph.assign(N+1, vector&lt;pair&lt;int, int&gt;&gt;());

    for(int i = 0; i &lt; E; i++){
        int src, dst, w;
        cin &gt;&gt; src &gt;&gt; dst &gt;&gt; w;
        graph[src].push_back({dst, w});
        graph[dst].push_back({src, w});
    }

    // 경유 지점
    int stop1, stop2;
    cin &gt;&gt; stop1 &gt;&gt; stop2;

    // 경우의 수는 1-&gt;stop1-&gt;stop2-&gt;N 또는 1-&gt;stop2-&gt;stop1-&gt;N
    int dist_two_stop = dijkstra(stop1, stop2);
    int ans = min(dijkstra(1, stop1) + dist_two_stop + dijkstra(stop2, N), dijkstra(1, stop2) + dist_two_stop + dijkstra(stop1, N));
    if(ans &gt;= INF) cout &lt;&lt; -1;
    else cout &lt;&lt; ans;
    return 0;
}

int dijkstra(int depart, int arrival){
    vector&lt;int&gt; distance(N+1, INF);
    // (거리, 노드번호)
    priority_queue&lt;pair&lt;int, int&gt;, vector&lt;pair&lt;int, int&gt;&gt;, greater&lt;&gt;&gt; pq;

    distance[depart] = 0;
    pq.push({0, depart});

    while(!pq.empty()){
        auto [cost, now] = pq.top();
        pq.pop();

        // 이미 더 짧은 경로 존재하면 skip
        if(distance[now] &lt; cost) continue;

        for(auto [next, weight] : graph[now]){
            int new_cost = cost + weight;
            if(new_cost &lt; distance[next]){
                distance[next] = new_cost;
                pq.push({new_cost, next});
            }
        }
    }
    return distance[arrival];
}</code></pre>
<hr>
<h2 id="🔎-문제-설명">🔎 문제 설명</h2>
<ul>
<li>정점 1번에서 N번까지 가는데, 주어진 두 정점(stop1, stop2)을 <strong>반드시 경유</strong>해야 한다.</li>
<li>경로 순서는 다음 두 가지가 가능:<ul>
<li>$1 \rightarrow \text{stop1} \rightarrow \text{stop2} \rightarrow N$</li>
<li>$1 \rightarrow \text{stop2} \rightarrow \text{stop1} \rightarrow N$</li>
</ul>
</li>
<li>이 두 경우 중 최단 경로의 길이를 구하라.</li>
</ul>
<hr>
<h2 id="⛔-내가-틀렸던-부분">⛔ 내가 틀렸던 부분</h2>
<h3 id="1-inf-값-오버플로우-주의">1. INF 값 오버플로우 주의</h3>
<ul>
<li>처음엔 <code>INF = 10^9</code>로 설정했는데,</li>
<li>최단거리끼리 3번 덧셈을 하면 $10^9 \times 3 = 3 \times 10^9$ 이 되어 <strong>int 오버플로우</strong>가 발생할 수 있었다.</li>
<li>→ <strong>수정</strong> : <code>INF = 10^9 / 3</code> 로 설정해서 여러 번 더해도 안전하도록 했다.</li>
</ul>
<h3 id="2-무방향-간선-입력-실수">2. 무방향 간선 입력 실수</h3>
<ul>
<li>입력을 받을 때 아래처럼 한 방향만 추가했음:<pre><code class="language-cpp">graph[src].push_back({dst, w});</code></pre>
</li>
<li>그러나 무방향이므로 반드시 반대 방향도 추가해야 한다:<pre><code class="language-cpp">graph[dst].push_back({src, w});</code></pre>
<h3 id="3-dijkstra-호출-최적화">3. dijkstra 호출 최적화</h3>
</li>
<li>내 풀이는 다익스트라를 5번 호출했음:<ul>
<li>dijkstra(1, stop1)</li>
<li>dijkstra(stop1, stop2)</li>
<li>dijkstra(stop2, N)</li>
<li>dijkstra(1, stop2)</li>
<li>dijkstra(stop1, N)</li>
</ul>
</li>
<li>그러나 아래처럼 하면 <strong>3번만</strong> 호출해도 모든 정보를 얻을 수 있다:<ul>
<li>다익스트라(1) → stop1, stop2, N까지 거리 얻음</li>
<li>다익스트라(stop1) → stop2, N까지 거리 얻음</li>
<li>다익스트라(stop2) → N까지 거리 얻음</li>
</ul>
</li>
</ul>
<p>이 문제에서는 시간 제한이 엄청 빡빡하지는 않아 다익스트라 5번 호출을 해도 문제가 없었으나, 더 타이트한 조건이라면 위와 같이 최적화할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 7572 - 간지 (C++) / 나머지 연산 기초 정리]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-7572-%EA%B0%84%EC%A7%80-C-%EB%82%98%EB%A8%B8%EC%A7%80-%EC%97%B0%EC%82%B0-%EA%B8%B0%EC%B4%88-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@g1fted_13/BOJ-7572-%EA%B0%84%EC%A7%80-C-%EB%82%98%EB%A8%B8%EC%A7%80-%EC%97%B0%EC%82%B0-%EA%B8%B0%EC%B4%88-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 06 Jun 2025 15:39:02 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/7572">https://www.acmicpc.net/problem/7572</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/273d9f3e-94d9-4c5c-8a61-07297e9e6964/image.jpeg" alt=""></p>
<h3 id="최초-풀이-날짜-2025-06-01">최초 풀이 날짜: 2025. 06. 01</h3>
<h3 id="재풀이-날짜-2025-06-06">재풀이 날짜: 2025. 06. 06</h3>
<h3 id="math-implementation-arithmetic">#math #implementation #arithmetic</h3>
<h2 id="최초-풀이-c">최초 풀이 (C++)</h2>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    int cha = N - 2013;

    // 지 구하기
    int ji = (5 + cha) % 12;
    if(ji &lt; 0) ji += 12;
    cout &lt;&lt; char(ji + &#39;A&#39;);

    // 간 구하기
    int gan = (9 + cha) % 10;
    if(gan &lt; 0) gan += 10;
    cout &lt;&lt; gan;

    return 0;
}</code></pre>
<h2 id="재풀이-c">재풀이 (C++)</h2>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    int cha = N - 2013;

    // 지 구하기
    int ji = ((5 + cha) % 12 + 12) % 12;
    cout &lt;&lt; char(ji + &#39;A&#39;);

    // 간 구하기
    int gan = ((9 + cha) % 10 + 10) % 10;
    cout &lt;&lt; gan;

    return 0;
}</code></pre>
<h2 id="코멘트">코멘트</h2>
<ul>
<li>분명히 중1 때 수월히 풀었던 문제인데..지금 보니 생각할 부분이 많은 문제</li>
<li>첫째로, <strong>1년이 갑자년이 아니라는 점</strong>을 헷갈리지 말아야 함!<ul>
<li>문제에서도 2013년이 F9년 인 점을 이용해서 계산하라고 힌트를 주고 있음.</li>
</ul>
</li>
<li>간지를 구해야 하는 입력 N과 간지를 알고 있는 2013년과의 차이를 통해 N년의 간지를 계산하면 되는데, 코드를 잘 짜놓고 나중에 내 코드를 다시 보니 나머지 연산이 헷갈리는 것이다!<ul>
<li>이 기회를 통해 나머지 연산을 잘 정리해보기로 했다.</li>
</ul>
</li>
</ul>
<h2 id="나머지-연산-기초">나머지 연산 기초</h2>
<ul>
<li>a % b 연산을 할 때, C와 Python 간에 연산의 결과에 차이가 생길 때가 있다. 바로 a 나 b의 부호가 음수일 때다. 아래의 예시를 통해 확인해보자.</li>
</ul>
<ol>
<li><strong>a &gt; 0 ,  b&gt;0</strong><pre><code class="language-python">10 // 3
10 % 3</code></pre>
</li>
</ol>
<hr>
<p>Python: 3, 1
C++: 3, 1</p>
<pre><code>당연하게도 a와 b가 모두 양수인 경우 두 언어 모두 우리가 아는 결과와 같다.

2.  **a &lt; 0 ,  b&gt;0**
```python
-10 // 3
-10 % 3
- - - - - - -
Python: -4, 2
C++: -3, -1</code></pre><p>이 문제에서 등장하는 케이스가 되겠다. (N &lt; 2013)
Python의 경우 나머지가 양수, C++의 경우 나머지가 음수가 나왔다.</p>
<ol start="3">
<li><strong>a &gt; 0 ,  b&lt;0</strong><pre><code class="language-python">10 // -3
10 % -3</code></pre>
</li>
</ol>
<hr>
<p>Python: -4, -2
C++: -3, 1</p>
<pre><code>이번에는 반대로 나머지가 Python이 음수, C++이 양수였다.

4.  **a &lt; 0 ,  b&lt;0**
```python
-10 // -3
-10 % -3
- - - - - - -
Python: 3, -1
C++: 3, -1</code></pre><p>이번에는 두 언어 모두 똑같이 나머지가 음수로 나왔다.</p>
<h2 id="음수-나머지에-대한-이해--코드-작성-팁">음수 나머지에 대한 이해 + 코드 작성 팁</h2>
<ul>
<li>위 예시들을 보면 알 수 있듯이, <strong>언어에 따라 동일한 a, b에 대해서도 % 연산 결과가 다를 수 있다.</strong></li>
<li>보통 우리는 나머지 연산 결과가 <strong>0 이상</strong>이길 기대하지만, a와 b 중 하나가 음수이면 <strong>나머지가 음수가 될 수 있음</strong>에 유의해야 한다.</li>
</ul>
<p>🎯 <strong>간지 문제 적용 예시</strong></p>
<ul>
<li>2013년을 기준으로 어떤 해의 간지(간, 지)를 구하려 할 때,<ul>
<li>기준 연도와의 차이를 계산하고,</li>
<li>이를 10과 12로 나눈 <strong>나머지</strong>를 통해 간과 지를 결정하는 아이디어를 사용했다.</li>
</ul>
</li>
<li>2013년의 ‘지’가 F(5)이므로<ul>
<li>차이가 +1인 2014년은 G(6),</li>
<li>차이가 -5인 2008년은 A(0)이 된다.</li>
</ul>
</li>
<li>그런데 차이가 -6인 2007년의 경우,<ul>
<li>(5-6) % 12는 Python에서는 <code>11</code>, C++에서는 <code>-1</code>이 되며,</li>
<li>지는 0부터 11까지 존재하므로, 음수가 나오면 인덱스 오류가 발생할 수 있다.</li>
<li>Python 연산의 결과로 나온 <code>11</code>은 우리가 원하는 2007년의 ‘지‘이며,  C++ 연산의 결과로 나온 <code>-1</code>은, 0번 인덱스보다 <code>1칸 이전</code>이라고 생각할 수 있다.</li>
</ul>
</li>
</ul>
<p>💡 <strong>그래서 해결책은?</strong></p>
<ul>
<li>나머지 연산 결과가 음수일 가능성을 제거하기 위해,<blockquote>
<p><strong>(a % b + b) % b</strong></p>
</blockquote>
</li>
</ul>
<p>를 사용하면 된다.</p>
<ul>
<li>이 식은 a % b가 음수일 경우 b를 더해서 양수로 만들고,
혹시나 b 이상이 되었을 경우 다시 % b로 조정해준다.</li>
<li>a % b의 결과가 이미 음이 아닐 경우(0 이상 b미만), b를 더해도 2b 미만이기 때문에 이 식의 결과는 여전히 a % b와 동일하다.</li>
<li>결과적으로 이 표현은 항상 <strong>0 이상 b 미만의 값</strong>을 만들어 준다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 2805 - 나무 자르기 (C++) / Parametric Search]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-2805-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-2805-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-C</guid>
            <pubDate>Wed, 04 Jun 2025 09:33:33 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2805">https://www.acmicpc.net/problem/2805</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/8e360c53-79a6-44cb-adbf-c9d900d2716d/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-06-04">문제를 푼 날짜: 2025. 06. 04</h3>
<h3 id="binary_search-parametric_search">#binary_search #parametric_search</h3>
<h3 id="parametric-search란">Parametric Search란?</h3>
<p>(<a href="https://ialy1595.github.io/post/parametric-search/">https://ialy1595.github.io/post/parametric-search/</a> 의 내용을 참고하였습니다.)</p>
<ul>
<li><strong><code>Parametric Search</code></strong>는 <strong>최댓값/최솟값을 구하는 최적화 문제</strong>를,
&quot;어떤 값 x가 조건을 만족하는가?&quot;라는 <strong>결정문제</strong>로 바꾼 뒤,</li>
<li><em>Binary Search*</em>를 이용해 답을 빠르게 찾는 문제 해결 기법이다.</li>
<li>실생활에서 정확한 값을 한 번에 구하지 않고 &quot;이 정도면 되나?&quot;를 반복해서 조금씩 조정하며 원하는 값을 찾는 식(예: 저울에 고기를 여러 번 올리며 200g 맞추기)의 사고라고 생각할 수 있다.</li>
<li>파라메트릭 서치를 쓸 수 있으려면,</li>
</ul>
<ol>
<li><strong>최댓값/최솟값을 구하는 문제</strong>여야 하고,</li>
<li><strong>조건을 만족하는 값들의 구간이 연속적</strong>이어야 하며,</li>
<li><strong>답의 범위가 이산적이거나(정수), 허용 오차가 있어야</strong> 한다.</li>
</ol>
<h3 id="아이디어">아이디어</h3>
<p>이 문제는 <strong>&quot;최댓값을 구하는 문제&quot;</strong>이고, 그 값을 직접 계산하기엔 복잡하다. 따라서 <strong>조건을 만족하는지 확인하는 결정 문제로 바꾼 후, 이분 탐색을 이용하는</strong> <code>Parametric Search</code> 기법을 사용한다.</p>
<blockquote>
<p>✔️ <code>H 높이로 잘랐을 때, 나무 길이의 합이 M 이상인가?</code>를 판단하는 결정 문제로 변형하고<br>✔️ 만족한다면 <code>더 높이 자를 수 있는지 탐색</code><br>✔️ 만족하지 못한다면 <code>톱 높이를 낮추는 방향으로 탐색</code></p>
</blockquote>
<h3 id="내-코드-c">내 코드 (C++)</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;
using ll = long long;

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, M;
    cin &gt;&gt; N &gt;&gt; M;

    vector&lt;int&gt; trees(N);

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; trees[i];
    }

    int low = 0, high = 1000000000;
    int ans;

    while(low &lt;= high){
        int mid = (low + high) / 2;

        ll sum = 0;
        for(auto &amp;tree: trees){
            sum += max(0, tree - mid);
        }

        // 높이를 mid로 설정했을 때 요건을 만족 -&gt; 정답 후보로 저장하고, 높이를 올리기
        if(sum &gt;= M) {
            ans = mid;
            low = mid + 1;
        }

        // 요건 만족 x -&gt; 높이를 낮춰봄
        else high = mid - 1;
    }

    cout &lt;&lt; ans;
    return 0;
}</code></pre>
<h3 id="분석">분석</h3>
<ul>
<li>시간복잡도: $O(N logH)$ -&gt; $H$는 나무의 최대 높이인 $10^9$.
-&gt; 시간 내에 충분히 들어옴!</li>
<li>처음에 sum을 int형으로 선언해서 WA가 떴고, 원인을 찾지 못했음.
-&gt; sum은 $10^6 \times 10^9 = 10^{15}$으로 int 범위 충분히 초과 가능!
-&gt; 항상 변수의 범위에 유의할 것</li>
<li>톱 높이는 최소 0부터, 최대는 문제에 주어진 나무 높이의 최댓값(= $10^9$)까지 가능하다.
→ 따라서 low = 0, high = 1,000,000,000으로 탐색 범위를 설정한다.</li>
<li><blockquote>
<p>최댓값을 $10^9$ 대신 입력된 나무 중 제일 높은 나무의 높이로 잡아도 된다. (조금 더 최적화된 코드)</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 11054 - 가장 긴 바이토닉 부분 수열 (C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-11054-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EB%B0%94%EC%9D%B4%ED%86%A0%EB%8B%89-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-11054-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EB%B0%94%EC%9D%B4%ED%86%A0%EB%8B%89-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-C</guid>
            <pubDate>Tue, 03 Jun 2025 05:59:09 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/11054">https://www.acmicpc.net/problem/11054</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/2ceee6e6-7ebf-4a5e-b8e7-a0a69926906c/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-06-03">문제를 푼 날짜: 2025. 06. 03</h3>
<h3 id="dp">#dp</h3>
<h3 id="아이디어">아이디어</h3>
<ul>
<li>각 인덱스를 기준으로 <strong>&quot;증가&quot;</strong>하는 부분 수열과, <strong>&quot;감소&quot;</strong>하는 부분 수열을 각각 계산해서 합치는 방식.</li>
<li>DP(동적 프로그래밍)를 두 번 적용!</li>
<li>DP1[i] : i번째 원소까지 고려했을 때, 해당 원소를 끝으로 하는 가장 긴 증가 부분 수열의 길이</li>
<li>DP2[i] : i번째 원소부터 시작해서, 해당 원소를 시작으로 하는 가장 긴 감소 부분 수열의 길이 (뒤에서부터 거꾸로!)</li>
<li>DP2를 &#39;뒤에서부터 센 가장 긴 증가 부분 수열&#39;로 생각.</li>
<li>각 인덱스 i에 대해 <code>DP1[i] + DP2[i] - 1</code> (<code>-1</code> 하는 이유는 i가 중복 포함되니까!)</li>
<li>이 값의 <code>최댓값</code>이 곧 가장 긴 바이토닉 부분 수열의 길이</li>
</ul>
<h2 id="내-풀이c">내 풀이(C++)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

int A[1010];
int DP1[1010];
int DP2[1010];

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    int max;
    cin &gt;&gt; N;

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; A[i];

        max = 0;
        for(int j = 0; j &lt; i; j++){
            if(A[j] &lt; A[i] &amp;&amp; DP1[j] &gt; max) max = DP1[j];
        }
        DP1[i] = max + 1;
    }

    for(int i = N-1; i &gt;= 0; i--){
        max = 0;
        for(int j = N -1; j &gt; i; j--){
            if(A[i] &gt; A[j] &amp;&amp; DP2[j] &gt; max) max = DP2[j];
        }
        DP2[i] = max + 1;
    }

    max = 0;

    for(int i = 0; i &lt; N; i++){
        if(DP1[i] + DP2[i] &gt; max) max = DP1[i] + DP2[i];
    }

    cout &lt;&lt; max - 1;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 11053 - 가장 긴 증가하는 부분 수열 (C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-11053-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-11053-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-C</guid>
            <pubDate>Tue, 03 Jun 2025 05:33:23 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/11053">https://www.acmicpc.net/problem/11053</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/1de61481-4a43-4058-a9e4-da70e9c1d6c0/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2024-01-11">문제를 푼 날짜: 2024. 01. 11</h3>
<h3 id="dp-lis">#dp #lis</h3>
<h2 id="아이디어">아이디어</h2>
<p>$DP[i] =$ $i$번째 원소가 마지막이 되도록 추가했을 때 최대 길이</p>
<p>####
<strong>Substring vs Subsequence 헷갈리지 말기!!</strong></p>
<ul>
<li>이 문제는 Subsequence를 구하는 문제</li>
</ul>
<h2 id="내-풀이c">내 풀이(C++)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

/*
DP + Backtracking
DP[i] : i번 쨰 원소를 추가했을 때의 최대 길이(이전까지의 원소들 중 자기보다 작은 값의 최대 DP값 + 1)
*/
int A[1010];
int DP[1010];

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    int max;
    cin &gt;&gt; N;

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; A[i];

        max = 0;
        for(int j = 0; j &lt; i; j++){
            if(A[j] &lt; A[i] &amp;&amp; DP[j] &gt; max){
                max = DP[j];
            }
        }
        DP[i] = max + 1;
    }

    max = 0;

    for(int i = 0; i &lt; N; i++){
        if(DP[i] &gt; max) max = DP[i];
    }

    cout &lt;&lt; max;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.2 (2.21~2.25)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.212.25</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.212.25</guid>
            <pubDate>Tue, 03 Jun 2025 05:07:05 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-221-examples--각각의-위상적-성질-정밀-분석-closed--open--perfect--bounded">✅ 2.21 Examples — 각각의 위상적 성질 정밀 분석 (Closed / Open / Perfect / Bounded)</h2>
<hr>
<h4 id="a-집합--z-in-mathbbc-mid-z--1-">(a) 집합: ${ z \in \mathbb{C} \mid |z| &lt; 1 }$</h4>
<ul>
<li><p><strong>Closed</strong>: X<br>→ $|z| = 1$에 있는 점들은 이 집합의 limit point이지만 포함되지 않음.  </p>
</li>
<li><p><strong>Open</strong>: O<br>→ 임의의 점 $p$에 대해, 반지름 $\varepsilon = 1 - |p|$인 neighborhood $N_\varepsilon(p)$가 집합에 완전히 포함됨.<br>→ 모든 점이 interior point.</p>
</li>
<li><p><strong>Perfect</strong>: X<br>→ closed가 아니므로 perfect일 수 없음. </p>
</li>
<li><p><strong>Bounded</strong>: O<br>→ 모든 점 $z$에 대해 $|z| &lt; 1$이므로, 중심 0, 반지름 1인 ball 안에 포함됨.</p>
</li>
</ul>
<hr>
<h4 id="b-집합--z-in-mathbbc-mid-z-le-1-">(b) 집합: ${ z \in \mathbb{C} \mid |z| \le 1 }$</h4>
<ul>
<li><p><strong>Closed</strong>: O<br>→ 극한값이 $|z| = 1$인 수열들이 모두 이 집합 안에 수렴함. 모든 limit point 포함.</p>
</li>
<li><p><strong>Open</strong>: X<br>→ 경계점 $z$ such that $|z| = 1$는 interior point가 아님.  </p>
</li>
<li><p><strong>Perfect</strong>: O<br>→ 집합은 닫혀 있고, 모든 점이 limit point.<br>→ 예: 원판 내부의 점들 근처에는 언제나 다른 점들이 있음. 경계의 점도 limit point.</p>
</li>
<li><p><strong>Bounded</strong>: O<br>→ 모든 $z$에 대해 $|z| \le 1$이므로 유계.</p>
</li>
</ul>
<hr>
<h4 id="c-집합-유한-집합-예-1-0-2-0">(<strong>c</strong>) 집합: 유한 집합, 예: ${(1, 0), (2, 0)}$</h4>
<ul>
<li><p><strong>Closed</strong>: O<br>→ 유한 집합은 limit point가 없음. (2.20 Corollary) 따라서 vacuously closed(공허하게 닫힘; 가정이 거짓이므로 항상 참).  </p>
</li>
<li><p><strong>Open</strong>: X<br>→ 어떤 점 $p$에 대해 반지름 $\varepsilon &gt; 0$인 근방은 집합의 다른 점을 포함하지 않음.<br>→ 즉, interior point가 없음.</p>
</li>
<li><p><strong>Perfect</strong>: X<br>→ limit point가 존재하지 않으므로, perfect 아님.</p>
</li>
<li><p><strong>Bounded</strong>: O<br>→ 유한 집합은 항상 어떤 유한한 반지름의 ball 안에 포함될 수 있음.</p>
</li>
</ul>
<hr>
<h4 id="d-집합-mathbbz-subset-mathbbr">(d) 집합: $\mathbb{Z} \subset \mathbb{R}$</h4>
<ul>
<li><p><strong>Closed</strong>: O<br>→ Limit Point가 없으므로(모든 점이 isolated) vacuously closed</p>
</li>
<li><p><strong>Open</strong>: X<br>→ 정수 $n$에 대해 아무리 작은 neighborhood $N_\varepsilon(n)$도 실수 $n + \delta \in \mathbb{R} \setminus \mathbb{Z}$ 포함함. 
(Interior Point의 정의: $E$에 대해 정의된 $p$에 대하여 <strong>$N \subset E$ 인</strong> neighborhood $N$ 이 존재하는 $p$) 
→ Interior point 없음</p>
</li>
<li><p><strong>Perfect</strong>: X<br>→ 집합 내 어떤 점도 limit point가 아님. (Every point is isolated)</p>
</li>
<li><p><strong>Bounded</strong>: X<br>→ $\mathbb{Z}$는 음의 무한대로도, 양의 무한대로도 발산 → 유계 아님.</p>
</li>
</ul>
<hr>
<h4 id="e-집합-e--1n-mid-n-in-mathbbn-">(e) 집합: $E = {1/n \mid n \in \mathbb{N} }$</h4>
<ul>
<li><p><strong>Closed</strong>: X<br>→ 0은 limit point이지만 집합에 포함되지 x</p>
</li>
<li><p><strong>Open</strong>: X<br>→ 어떤 $1/n$의 neighborhood도 다른 원소들을 포함하지 않음.</p>
</li>
<li><p><strong>Perfect</strong>: X<br>→ 애초에 not closed여서 안 됨.</p>
</li>
<li><p><strong>Bounded</strong>: O<br>→ $0 &lt; 1/n \le 1$ → 반지름 1인 ball 안에 전부 포함됨.</p>
</li>
</ul>
<hr>
<h4 id="f-집합-전체-공간-mathbbr2">(f) 집합: 전체 공간 $\mathbb{R}^2$</h4>
<ul>
<li><p><strong>Closed</strong>: O<br>→ 전체 공간은 limit point 포함 조건을 자동으로 만족.</p>
</li>
<li><p><strong>Open</strong>: O<br>→ 모든 점에 대해 어떤 반지름의 neighborhood도 전체 공간 안에 있음.</p>
</li>
<li><p><strong>Perfect</strong>: O<br>→ 닫혀 있고, 모든 점이 limit point. 즉, interior에도 점이 있고, 극한 접근 가능.</p>
</li>
<li><p><strong>Bounded</strong>: X<br>→ 어떤 반지름의 ball도 전체 공간을 포함할 수 없음 → 유계 아님.</p>
</li>
</ul>
<hr>
<h4 id="g-집합-실수-구간-a-b-subset-mathbbr">(g) 집합: 실수 구간 $(a, b) \subset \mathbb{R}$</h4>
<ul>
<li><p><strong>Closed</strong>: X<br>→ 경계점 $a$, $b$는 limit point이지만 포함되어 있지 않음.</p>
</li>
<li><p><strong>Open</strong>:  </p>
<ul>
<li>O (in $\mathbb{R}^1$)<br>→ 모든 점 $x \in (a, b)$에 대해 $N_\varepsilon(x) \subset (a, b)$</li>
<li>X (in $\mathbb{R}^2$)<br>→ $x = 0$ 위에 있는 1차원 집합일 뿐이므로 2차원 열린 집합이 아님.</li>
</ul>
</li>
<li><p><strong>Perfect</strong>: X<br>→ 닫혀 있지 않음 → perfect일 수 없음</p>
</li>
<li><p><strong>Bounded</strong>: O<br>→ 유한 길이의 구간은 항상 bounded</p>
</li>
</ul>
<hr>
<h2 id="✅-222-de-morgans-law-for-complements-드-모르간-법칙">✅ 2.22 De Morgan’s Law for Complements (드 모르간 법칙)</h2>
<p><strong>Theorem</strong><br>Collection ${E_\alpha}$ 에 대해 다음이 성립한다:
$$
\left( \bigcup_\alpha E_\alpha \right)^c = \bigcap_\alpha E_\alpha^c
$$</p>
<p><strong>설명 및 증명</strong>  </p>
<ul>
<li>좌변을 $A = \left( \bigcup_\alpha E_\alpha \right)^c$,<br>우변을 $B = \bigcap_\alpha E_\alpha^c$ 라 하자.</li>
<li>이제 $A \subset B$, $B \subset A$ 임을 모두 보이면 $A = B$임을 알 수 있다.</li>
</ul>
<p><strong>(1) $A \subset B$</strong><br>$x \in A$ 라면, $x \notin \bigcup_\alpha E_\alpha$<br>→ 모든 $\alpha$에 대해 $x \notin E_\alpha$<br>→ 모든 $\alpha$에 대해 $x \in E_\alpha^c$<br>→ $x \in \bigcap_\alpha E_\alpha^c = B$</p>
<p><strong>(2) $B \subset A$</strong><br>$x \in B$ 라면, 모든 $\alpha$에 대해 $x \in E_\alpha^c$<br>→ 모든 $\alpha$에 대해 $x \notin E_\alpha$<br>→ $x \notin \bigcup_\alpha E_\alpha$<br>→ $x \in \left( \bigcup_\alpha E_\alpha \right)^c = A$</p>
<p><strong>결론</strong><br>$$
\left( \bigcup_\alpha E_\alpha \right)^c = \bigcap_\alpha E_\alpha^c
$$</p>
<hr>
<h2 id="✅-223-열린-집합과-닫힌-집합의-여집합-관계">✅ 2.23 열린 집합과 닫힌 집합의 여집합 관계</h2>
<p><strong>Theorem</strong>  </p>
<blockquote>
<p><strong>집합 $E$는 open $\Leftrightarrow$ $E^c$는 closed</strong></p>
</blockquote>
<h4 id="📝-proof">📝 Proof</h4>
<h4 id="⇒-방향-e가-open이면-ec는-closed">(⇒ 방향) $E$가 open이면, $E^c$는 closed</h4>
<ul>
<li>$x$가 $E^c$의 limit point라고 하자.</li>
<li>그러면 모든 $x$의 neighborhood는 $E^c$의 점을 하나 이상 포함.</li>
<li>이는 곧 $x$가 $E$의 interior point가 아님을 의미.</li>
<li>그런데 $E$는 open이므로 모든 점은 interior point이어야 함 → $x \notin E$</li>
<li>즉 $x \in E^c$ → limit point가 포함됨 → $E^c$는 closed</li>
</ul>
<h4 id="⇐-방향-ec가-closed이면-e는-open">(⇐ 방향) $E^c$가 closed이면, $E$는 open</h4>
<ul>
<li>$x \in E$ 라고 하자 → $x \notin E^c$</li>
<li>$x$는 $E^c$의 limit point가 아니므로, 어떤 neighborhood $N$이 존재해서 $N \cap E^c = \emptyset$</li>
<li>따라서 $N \subset E$ → $x$는 $E$의 interior point</li>
<li>모든 $x \in E$에 대해 interior point이므로 $E$는 open</li>
</ul>
<hr>
<h3 id="📌-corollary">📌 Corollary</h3>
<blockquote>
<p><strong>$F$가 closed ⇔ $F^c$가 open</strong></p>
</blockquote>
<h2 id="✅-224-theorem-openclosed-집합의-연산">✅ 2.24 Theorem: Open/Closed 집합의 연산</h2>
<p>다음이 성립한다:</p>
<ol>
<li><p><strong>(a)</strong> 임의의 open set들의 collection ${G_\alpha}$에 대해,<br>$$\bigcup_\alpha G_\alpha$$<br>는 open set이다.</p>
</li>
<li><p><strong>(b)</strong> 임의의 closed set들의 collection ${F_\alpha}$에 대해,<br>$$\bigcap_\alpha F_\alpha$$<br>는 closed set이다.</p>
</li>
<li><p><strong>(</strong>c<strong>)</strong> 유한 개의 open set $G_1, \dots, G_n$에 대해,<br>$$\bigcap_{i=1}^n G_i$$<br>는 open set이다.</p>
</li>
<li><p><strong>(d)</strong> 유한 개의 closed set $F_1, \dots, F_n$에 대해,<br>$$\bigcup_{i=1}^n F_i$$<br>는 closed set이다.</p>
</li>
</ol>
<hr>
<h3 id="📝-proof-sketch">📝 Proof Sketch</h3>
<ul>
<li><strong>(a)</strong> $x \in \bigcup G_\alpha$ 이면 어떤 $\alpha$에 대해 $x \in G_\alpha$이고, $x$는 그 $G_\alpha$의 interior point → $x$는 전체 합집합의 interior point.</li>
<li><strong>(b)</strong> 드 모르간 법칙:<br>$$\left( \bigcap_\alpha F_\alpha \right)^c = \bigcup_\alpha F_\alpha^c$$<br>우변은 open이므로, $\bigcap_\alpha F_\alpha$ 는 closed.</li>
<li><strong>(</strong>c<strong>)</strong> $x \in \bigcap G_i$ 에 대해 각 $G_i$는 open이므로, 반지름 $r_i$의 neighborhood $N_i \subset G_i$ 존재.<br>$$r = \min(r_1, \dots, r_n)$$<br>으로 잡은 neighborhood $N$은 모든 $G_i$에 포함되므로 $x$는 interior point.</li>
<li><strong>(d)</strong> (<strong>c</strong>) 의 여집합을 취해 적용.</li>
</ul>
<hr>
<h2 id="✅-225-examples-유한성과-무한성의-차이">✅ 2.25 Examples: 유한성과 무한성의 차이</h2>
<ul>
<li><strong>(</strong>c<strong>)</strong>, (d) 조건은 유한한 경우에만 성립하며, <strong>무한한 경우엔 깨진다.</strong></li>
</ul>
<h3 id="❗-반례-무한-교집합">❗ 반례: 무한 교집합</h3>
<ul>
<li>$G_n = (-1/n, 1/n)$ 은 각각 open이다.</li>
<li>$$\bigcap_{n=1}^\infty G_n = {0}$$</li>
<li>하지만 ${0}$ 은 open이 아니다.</li>
</ul>
<p>👉 따라서 무한히 많은 open set들의 교집합은 open이 아닐 수 있음.</p>
<h3 id="❗-반례-무한-합집합">❗ 반례: 무한 합집합</h3>
<ul>
<li>마찬가지로, 무한히 많은 closed set들의 합집합은 closed가 아닐 수 있음.</li>
<li>예: $$F_n = \left[ -1 + \frac{1}{n},\ 1 - \frac{1}{n} \right] \quad \text{for } n = 2, 3, 4, \dots$$
은 각각 closed 이다.</li>
<li>하지만 
$$
F = \bigcup_{n=2}^{\infty} F_n = (-1, 1)
$$
은 open이다.
👉 따라서 무한히 많은 closed set들의 합집합은 closed가 아닐 수 있음.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 31263 - 대한민국을 지키는 가장 긴 힘 (C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-31263-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%84-%EC%A7%80%ED%82%A4%EB%8A%94-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%ED%9E%98-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-31263-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%84-%EC%A7%80%ED%82%A4%EB%8A%94-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%ED%9E%98-C</guid>
            <pubDate>Tue, 03 Jun 2025 04:22:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/31263">https://www.acmicpc.net/problem/31263</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/d8143a29-da36-48ea-800d-34cbccc07dd8/image.png" alt="">
<img src="https://velog.velcdn.com/images/g1fted_13/post/43bcf978-1cc5-4c95-9f7f-edd0f3b0165d/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-25-05-27">문제를 푼 날짜: 25. 05. 27.</h3>
<h3 id="greedy">#greedy</h3>
<h2 id="아이디어">아이디어</h2>
<ul>
<li>꽤 오래 고민한 문제였지만 쉽지 않아 Editorial 및 GPT의 도움을 받았다.</li>
<li>병사의 수를 최소화해야 하므로, 왼쪽부터 탐색하며 가능한 한 긴 유효 조각(최대 3자리)을 만든다.</li>
<li>현재 위치(pos)에서 3자리까지 조각을 시도하며, 유효하지 않으면 길이를 줄인다.<ul>
<li>유효 조건: 조각은 0으로 시작하지 않고, 정수 값이 1 이상 641 이하.</li>
<li>0으로는 시작할 수 없으므로 이전 유효 조각의 마지막 숫자로 현재 위치(pos)를 이동 (이전 유효조각은 길이가 1만큼 줄어도 여전히 유효)</li>
</ul>
</li>
<li>가장 긴 유효 조각을 찾으면 그만큼 이동(pos += len), 병사 수 +1</li>
<li>이를 문자열 끝까지 반복하여 총 병사 수를 구한다.</li>
</ul>
<h2 id="pseudocode-greedy">Pseudocode (Greedy)</h2>
<pre><code>pos ← 0
cnt ← 0
n ← |s|   # 문자열 길이

while pos &lt; n do
    cnt ← cnt + 1
    len ← min(3, n − pos)

    loop  # 이 안에서 “한 조각”을 확정할 때까지 반복
        # 1) 새 조각이 &#39;0&#39;으로 시작하면 → 잘못 끊어진 경계이므로
        if s[pos] == &#39;0&#39; then
            pos ← pos − 1
            len ← min(3, n − pos)
            continue    # 다시 같은 조각에서 재시도
        end if

        # 2) s[pos..pos+len−1] 을 숫자로 변환
        num ← 0
        for i in 0 … len−1 do
            num ← num * 10 + (s[pos+i] − &#39;0&#39;)
        end for

        # 3) 1 ≤ num ≤ 641 이면 이 길이로 확정하고 다음 조각으로
        if num ≤ 641 then
            pos ← pos + len
            break      # while‐loop 빠져나가서 다음 조각 시작
        else
            len ← len − 1  # 너무 크면 한 글자 줄여 다시 검사
        end if
    end loop
end while

print cnt</code></pre><h2 id="c-풀이-greedy">C++ 풀이 (Greedy)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    string s;
    cin &gt;&gt; s;

    int pos = 0;
    int cnt = 0;



    while(pos &lt; int(s.length())){
        cnt++;
        int len = min(3, N - pos);

        while(1){
            int num = s[pos] - &#39;0&#39;;
            if(num == 0){
                pos--;
                len = min(3, N - pos);
                continue;
            }

            for(int i = pos + 1; i &lt; pos + len; i++){
                num = num * 10 + (s[i] - &#39;0&#39;);
            }

            if(num &lt;= 641){
                pos += len;
                break;
            } else len--;
        }
    }

    cout &lt;&lt; cnt;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 12891 - DNA 비밀번호 (C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-12891-DNA-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-12891-DNA-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-C</guid>
            <pubDate>Mon, 02 Jun 2025 08:21:13 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/12891">https://www.acmicpc.net/problem/12891</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/1b51de01-08cd-4db3-bc3b-3c887c0ad9e1/image.png" alt=""></p>
<h3 id="최초-풀이-2025-06-01">최초 풀이: 2025. 06. 01.</h3>
<h3 id="sliding_window-string">#sliding_window #string</h3>
<h2 id="아이디어">아이디어</h2>
<ul>
<li>전형적인 &#39;sliding_window&#39; 문제이다.</li>
<li>0번째 index부터 시작하는 길이 |P|의 window를 잡아서 A, C, G, T 의 개수를 기록하고, 한 칸씩 이동하며 없어지는 문자와 새로 들어오는 문자를 확인하여 개수를 변화시켜주자.</li>
</ul>
<h2 id="substrings-vs-subsequences">Substrings vs Subsequences</h2>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/d052c19e-69c3-4a2f-b425-4f8ff2c61546/image.jpg" alt=""></p>
<ul>
<li><strong>Substring</strong>: 원래의 문자열에서 <strong>연속된 구간</strong>을 떼어낸 부분 문자열이다. <ul>
<li>예: <code>&quot;abcdef&quot;</code>에서 <code>&quot;bcd&quot;</code>는 substring이다. </li>
</ul>
</li>
<li><strong>Subsequence</strong>: 원래의 문자열에서 일부 문자를 선택하여 <strong>순서만 유지한 채 나열</strong>한 부분 문자열이다. 연속적일 필요는 없다.<ul>
<li>예: <code>&quot;abcdef&quot;</code>에서 <code>&quot;acf&quot;</code>는 subsequence이다. (연속되지 않아도 됨)</li>
</ul>
</li>
</ul>
<p>이 문제에서는 <strong>Substring</strong>을 요구한다는 점에 주의하자! (<strong>Subsequence</strong>가 아님!)</p>
<h2 id="최초-풀이c내-풀이">최초 풀이(C++)(내 풀이)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int S, P;
    cin &gt;&gt; S &gt;&gt; P;

    string DNA;
    cin &gt;&gt; DNA;

    int A, C, G, T;
    cin &gt;&gt; A &gt;&gt; C &gt;&gt; G &gt;&gt; T;

    int num_A = 0, num_C = 0, num_G = 0, num_T = 0;
    int ans = 0;
    for(int i = 0; i &lt; P; i++){
        if(DNA[i] == &#39;A&#39;) num_A++;
        else if(DNA[i] == &#39;C&#39;) num_C++;
        else if(DNA[i] == &#39;G&#39;) num_G++;
        else if(DNA[i] == &#39;T&#39;) num_T++;
    }

    if(num_A &gt;= A &amp;&amp; num_C &gt;= C &amp;&amp; num_G &gt;= G &amp;&amp; num_T &gt;= T) ans++;

    for(int i = P; i &lt; S; i++){
        if(DNA[i - P] == &#39;A&#39;) num_A--;
        else if(DNA[i - P] == &#39;C&#39;) num_C--;
        else if(DNA[i - P] == &#39;G&#39;) num_G--;
        else if(DNA[i - P] == &#39;T&#39;) num_T--;

        if(DNA[i] == &#39;A&#39;) num_A++;
        else if(DNA[i] == &#39;C&#39;) num_C++;
        else if(DNA[i] == &#39;G&#39;) num_G++;
        else if(DNA[i] == &#39;T&#39;) num_T++;

        if(num_A &gt;= A &amp;&amp; num_C &gt;= C &amp;&amp; num_G &gt;= G &amp;&amp; num_T &gt;= T) ans++;
    }

    cout &lt;&lt; ans;
    return 0;
}</code></pre>
<h2 id="개선된-풀이by-gpt">개선된 풀이(by GPT)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

// 문자 인덱스 매핑 함수
int idx(char c) {
    if (c == &#39;A&#39;) return 0;
    if (c == &#39;C&#39;) return 1;
    if (c == &#39;G&#39;) return 2;
    return 3; // &#39;T&#39;
}

// 현재 윈도우의 카운트가 조건을 만족하는지 검사
bool is_valid(int count[], int required[]) {
    for (int i = 0; i &lt; 4; ++i) {
        if (count[i] &lt; required[i]) return false;
    }
    return true;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int S, P;
    cin &gt;&gt; S &gt;&gt; P;
    string DNA;
    cin &gt;&gt; DNA;

    int required[4]; // A, C, G, T가 최소 몇 개 필요한지
    for (int i = 0; i &lt; 4; ++i) cin &gt;&gt; required[i];
    int count[4] = {0,}; // 현재 윈도우의 A, C, G, T 개수

    // 초기 윈도우 설정
    for (int i = 0; i &lt; P; ++i) {
        count[idx(DNA[i])]++;
    }

    int ans = 0;
    if (is_valid(count, required)) ans++;

    // 슬라이딩 윈도우
    for (int i = P; i &lt; S; ++i) {
        count[idx(DNA[i - P])]--; // 왼쪽 문자 제거
        count[idx(DNA[i])]++;     // 오른쪽 문자 추가
        if (is_valid(count, required)) ans++;
    }

    cout &lt;&lt; ans &lt;&lt; &#39;\n&#39;;
    return 0;
}
</code></pre>
<ul>
<li>내 풀이를 작성할 때 main문이 지저분하다고 생각하였는데, 이런 식으로 함수를 이용하면 조금 더 깔끔하게 풀이를 적을 수 있을 것 같다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 1920 - 수 찾기 (C++) + Binary Search]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-1920-%EC%88%98-%EC%B0%BE%EA%B8%B0-C-Binary-Search</link>
            <guid>https://velog.io/@g1fted_13/BOJ-1920-%EC%88%98-%EC%B0%BE%EA%B8%B0-C-Binary-Search</guid>
            <pubDate>Thu, 22 May 2025 08:52:39 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1920">https://www.acmicpc.net/problem/1920</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/31518c98-c03b-4051-b0f5-290bd8553029/image.png" alt=""></p>
<h3 id="최초-풀이-2023-12-15">최초 풀이: 2023. 12. 15.</h3>
<h3 id="재풀이-2025-05-22">재풀이: 2025. 05. 22.</h3>
<h3 id="data_structures-sorting-binary_search-set-hash_set">#data_structures #sorting #binary_search #set #hash_set</h3>
<h2 id="아이디어">아이디어</h2>
<p>$O(N)$의 시간복잡도를 가진 Linear Search(선형 탐색)를 이용한다면 최악의 경우 $M$ 개의 수에 대해 $A$의 0번째부터 $N-1$ 번 째 원소까지 탐색하게 되므로 $O(NM)$ 의 시간복잡도를 가지게 되어 시간초과가 발생한다.</p>
<p>우리는 단순히 특정 값이 $A$에 포함되었는지의 여부가 궁금하므로 $O(N)$의 시간복잡도를 가진 <strong>Binary Search(이진탐색)</strong> 을 이용하여 시간복잡도를 개선할 수 있다. Binary Search를 하기 위해서는 먼저 $A$를 정렬해야 하므로 이때 시간복잡도는 $O((N+M)logN)$ 이다.</p>
<h2 id="최초풀이-binary-search-직접-구현">최초풀이 (Binary Search 직접 구현)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;

int arr[100001];
int N;

int BinarySearch(int x);

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int M, tmp;
    cin &gt;&gt; N;

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; arr[i];
    }

    sort(arr, arr + N);

    cin &gt;&gt; M;

    for(int i = 0; i &lt; M; i++){
        cin &gt;&gt; tmp;
        if(BinarySearch(tmp) == -1)
         cout &lt;&lt; 0 &lt;&lt; &#39;\n&#39;;
        else
         cout &lt;&lt; 1 &lt;&lt; &#39;\n&#39;;
    }
    return 0;
}

int BinarySearch(int x){
    int low = 0, high = N-1;
    int mid;

    while(low &lt;= high){
        mid = (low + high) / 2;
        if(arr[mid] &lt; x) low = mid + 1;
        else if(arr[mid] &gt; x) high = mid - 1;
        else return mid;
    }
    return -1;
}</code></pre>
<h2 id="재풀이-내장함수-이용">재풀이 (내장함수 이용)</h2>
<pre><code class="language-cpp">/*
auto lower = lower_bound(vec.begin(), vec.end(), key);
-&gt; 벡터에서 최초의 key 이상의 값을 iterator 형태로 저장
auto upper = upper_bound(vec.begin(), vec.end(), key);
-&gt; 벡터에서 최초의 key 초과값을 iterator 형태로 저장장
*/
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    vector&lt;int&gt; A(N, 0);
    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; A[i];
    }

    sort(A.begin(), A.end());

    int M;
    cin &gt;&gt; M;

    int num;
    for(int i = 0; i &lt; M; i++){
        cin &gt;&gt; num;
        auto low = lower_bound(A.begin(), A.end(), num);
        if(low - A.begin() == N || *low != num) cout &lt;&lt; 0;
        else cout &lt;&lt; 1;
        cout &lt;&lt; &#39;\n&#39;;
    }
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 25180 - 썸 팰린드롬 (C++)]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-25180-%EC%8D%B8-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-C</link>
            <guid>https://velog.io/@g1fted_13/BOJ-25180-%EC%8D%B8-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-C</guid>
            <pubDate>Thu, 22 May 2025 07:56:22 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/25180">https://www.acmicpc.net/problem/25180</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/67319c9a-7632-410f-b07d-e6d2fed8366f/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-25-05-22">문제를 푼 날짜: 25. 05. 22.</h3>
<h3 id="math-greedy">#math #greedy</h3>
<h2 id="아이디어">아이디어</h2>
<p>팰린드롬을 만들 때 <strong>가장 큰 숫자 9</strong>를 최대한 활용하면 자릿수를 최소화할 수 있다.</p>
<ul>
<li>앞뒤로 <code>9</code>를 한 번씩 추가하면(총 2자리 추가) 자리 합이 $18$만큼 올라감.</li>
<li>따라서 우선 $\frac{N}{18}$번 만큼 앞뒤에 <code>9</code>를 붙여준다.</li>
</ul>
<h2 id="세부-풀이-과정">세부 풀이 과정</h2>
<ol>
<li><p>$p = \displaystyle\left\lfloor \frac{N}{18} \right\rfloor$</p>
<ul>
<li>이때까지 추가된 자리 수는 $2p$</li>
</ul>
</li>
<li><p>나머지 $r = N \bmod 18$ 에 따라 추가 자리 수를 더함</p>
<ul>
<li><p>$r=0$</p>
<ul>
<li>더할 필요 없음 → $+0$</li>
</ul>
</li>
<li><p>$0 &lt; r \le 9$</p>
<ul>
<li>가운데 한 자리만 추가 → $+1$</li>
</ul>
</li>
<li><p>$10 \le r &lt; 18$</p>
<ul>
<li>$r$ 짝수 → 앞뒤에 한 번씩 추가 → $+2$</li>
<li>$r$ 홀수 → 앞뒤 + 가운데 한 자리 → $+3$</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>최종 답은</p>
<p>$$
\underbrace{2p}_{\substack{\text{9로 채운}\\text{기본 자리 수}}}
;+;
\begin{cases}
0, &amp; r=0,\
1, &amp; 0&lt;r\le9,\
2, &amp; 10\le r&lt;18,;r\text{ 짝수},\
3, &amp; 10\le r&lt;18,;r\text{ 홀수}.
\end{cases}
$$</p>
<h2 id="내-코드">내 코드</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    int ans = (N / 18) * 2;

    if(N % 18 == 0) cout &lt;&lt; ans;
    else if(N % 18 &lt;= 9) cout &lt;&lt; ans + 1;
    else if((N % 18) % 2 == 0) cout &lt;&lt; ans + 2;
    else cout &lt;&lt; ans + 3;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.2 (2.18~2.20)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.182.20</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.182.20</guid>
            <pubDate>Sun, 18 May 2025 16:06:56 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-218-definition-metric-space의-위상-개념">✅ 2.18 Definition (Metric Space의 위상 개념)</h2>
<h4 id="a-neighborhood-근방">(a) Neighborhood (근방)</h4>
<ul>
<li>어떤 점 $p$의 반지름 $r &gt; 0$인 neighborhood는:
$$
N_r(p) = { q \in X \mid d(p, q) &lt; r }
$$</li>
<li>$\mathbb{R}^1$에서는 열린 구간, $\mathbb{R}^2$에서는 원 내부에 해당.</li>
</ul>
<hr>
<h4 id="b-limit-point-극한점">(b) Limit point (극한점)</h4>
<ul>
<li>$p$의 <strong>모든 neighborhood</strong>가 $E$의 <strong>점 $q \ne p$</strong> 를 포함하면 $p$는 $E$의 <strong>limit point</strong>.</li>
<li>예: $E = {1/n}$ → 0은 $E$의 극한점.</li>
</ul>
<hr>
<h4 id="c-isolated-point-고립점">(<strong>c</strong>) Isolated point (고립점)</h4>
<ul>
<li>$E$ 안에 있으면서 <strong>limit point가 아닌 점</strong>.</li>
<li>예: $E = {1, 2, 3}$ → 모든 점은 고립점.</li>
</ul>
<hr>
<h4 id="d-closed-set-닫힌-집합">(d) Closed set (닫힌 집합)</h4>
<ul>
<li><strong>모든 limit point를 포함</strong>하는 집합.</li>
<li>예: $[0,1]$은 closed, $(0,1)$은 not closed.</li>
</ul>
<hr>
<h4 id="e-interior-point-내점">(e) Interior point (내점)</h4>
<ul>
<li>$p \in E$이고, 어떤 neighborhood $N_r(p) \subset E$이면 $p$는 interior point.</li>
</ul>
<hr>
<h4 id="f-open-set-열린-집합">(f) Open set (열린 집합)</h4>
<ul>
<li><strong>모든 point가  interior point</strong>일 때 $E$는 열린 집합.</li>
<li>예: $(0,1)$은 open, $[0,1]$은 open 아님.</li>
<li>주의! : closed와 open은 서로 반대되는 개념이 아님.</li>
</ul>
<hr>
<h4 id="g-complement-여집합">(g) Complement (여집합)</h4>
<ul>
<li>$E^c = { p \in X \mid p \notin E }$</li>
</ul>
<hr>
<h4 id="h-perfect-set-완전-집합">(h) Perfect set (완전 집합)</h4>
<ul>
<li>$E$가 <strong>closed</strong>이고, <strong>모든 점이 limit point</strong>이면 perfect.</li>
</ul>
<hr>
<h4 id="i-bounded-set-유계-집합">(i) Bounded set (유계 집합)</h4>
<ul>
<li>어떤 실수 $M$과 점 $q$가 존재하여:
$$
d(p, q) &lt; M \quad \text{for all } p \in E
$$</li>
<li>즉, $E$ 전체가 어떤 볼 안에 들어가는 경우.</li>
</ul>
<hr>
<h4 id="j-dense-set-조밀-집합">(j) Dense set (조밀 집합)</h4>
<ul>
<li><strong>$X$의 모든 점이 $E$의 limit point이거나 $E$의 point</strong>이면, $E$는 $X$에서 조밀.</li>
</ul>
<hr>
<h2 id="✅-219-theorem-every-neighborhood-is-an-open-set">✅ 2.19 Theorem: Every neighborhood is an open set</h2>
<blockquote>
<p><strong>어떤 점 $p$에 대한 반지름 $r$의 neighborhood $N_r(p)$는 항상 open set이다.</strong></p>
</blockquote>
<hr>
<h4 id="📝-proof-증명">📝 Proof (증명)</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/5383f1c2-dd0c-4fa3-b133-57af22ba2fb6/image.jpeg" alt=""></p>
<ul>
<li>p의 neighborhood $E = N_r(p)$라고 하자.</li>
<li>$q \in E$인 임의의 점을 잡자.</li>
<li>$d(p, q) &lt; r$이므로, 어떤 $h &gt; 0$가 존재하여:
$$
d(p, q) = r - h
$$</li>
<li>이제 $q$ 중심의 neighborhood $N_h(q)$를 생각하자.  </li>
<li>이 안의 임의의 점 $s$는 $d(q, s) &lt; h$를 만족하므로,</li>
</ul>
<p>삼각부등식에 의해:
$$
d(p, s) \le d(p, q) + d(q, s) &lt; r - h + h = r
$$</p>
<p>즉, $s \in N_r(p)$이므로:
$$
N_h(q) \subset N_r(p)
$$</p>
<ul>
<li>따라서 $q$는 $N_r(p)$의 interior point이다.</li>
<li>모든 $q \in N_r(p)$가 interior point이므로, $N_r(p)$는 open set이다.</li>
</ul>
<hr>
<h2 id="✅-220-theorem-limit-point-주변에는-무한히-많은-점이-있다">✅ 2.20 Theorem: Limit point 주변에는 무한히 많은 점이 있다</h2>
<blockquote>
<p><strong>어떤 점 $p$가 집합 $E$의 limit point라면,<br>$p$의 모든 neighborhood는 $E$의 무한히 많은 점들을 포함한다.</strong></p>
</blockquote>
<hr>
<h4 id="📝-proof-by-contradiction">📝 Proof (by contradiction)</h4>
<ol>
<li><p>$p$가 $E$의 limit point라고 하자.</p>
</li>
<li><p>그런데 어떤 neighborhood $N$이 $E$의 유한 개의 point들만 포함한다고 가정하자:
$$
N \cap E = { q_1, \dots, q_n } \quad (q_i \ne p)
$$</p>
</li>
<li><p>이 점들 중 $p$와의 최소 거리를 $r$이라 하자:
$$
r = \min{ d(p, q_1), \dots, d(p, q_n) } &gt; 0
$$</p>
</li>
<li><p>이제 반지름 $r$인 neighborhood $N_r(p)$를 고려하자.<br>그런데 이 안에는 $q_1, \dots, q_n$ 중 아무 점도 포함되지 않음.
 ($q_1, \dots, q_n$ 는 $p$로부터 거리가 $r$ 이상이기 때문)</p>
</li>
<li><p>따라서 $N_r(p) \cap E = \emptyset$</p>
</li>
</ol>
<p>→ 이는 <strong>limit point의 정의에 모순</strong>.  ($E$의 limit point $p$의 모든 neighborhood는 $p$가 아닌 $E$의 point $q$를 포함한다)
→ 즉, <strong>모든 neighborhood에는 무한히 많은 $E$의 점이 있어야 한다.</strong></p>
<hr>
<h3 id="📌-corollary">📌 Corollary</h3>
<blockquote>
<p><strong>유한한 집합은 limit point를 가질 수 없다.</strong></p>
</blockquote>
<ul>
<li>왜냐하면 아무리 neighborhood를 키워도 그 안에 유한한 점밖에 없기 때문.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.2 (2.15~2.17)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.152.17</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.152.17</guid>
            <pubDate>Sun, 18 May 2025 16:06:43 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-215-definition-metric-space-거리공간">✅ 2.15 Definition (Metric Space, 거리공간)</h2>
<p>집합 $X$에 대해, 모든 두 점 $p, q \in X$에 대해 <strong>metric(거리함수) $d(p, q)$</strong> 가 정의되어 있고 아래 조건들을 만족할 때, $(X, d)$를 <strong>metric space(거리공간)</strong> 라고 한다.</p>
<ol>
<li><strong>양의 성질 (Positivity)</strong>:  
$d(p, q) &gt; 0$ if $p \ne q$, and $d(p, p) = 0$</li>
<li><strong>대칭성 (Symmetry)</strong>:  
$d(p, q) = d(q, p)$</li>
<li><strong>삼각 부등식 (Triangle Inequality)</strong>:  
$d(p, q) \le d(p, r) + d(r, q)$ for all $r \in X$</li>
</ol>
<p>이러한 조건을 만족하는 함수 $d$를 <strong>metric</strong> 또는 <strong>distance function</strong> 이라고 한다.</p>
<hr>
<h2 id="✅-216-examples-metric-space의-예시">✅ 2.16 Examples (Metric Space의 예시)</h2>
<ul>
<li><p>대표적 예시: <strong>유클리드 공간 $\mathbb{R}^k$</strong></p>
<ul>
<li>특히 $\mathbb{R}^1$ (실수 직선), $\mathbb{R}^2$ (복소수 평면)</li>
<li>거리 함수: $d(x, y) = |x - y|$</li>
</ul>
</li>
<li><p>중요한 성질:</p>
<ul>
<li><strong>Metric space의 부분집합도 자체로 metric space가 된다.</strong></li>
<li>즉, $Y \subset X$ 이면 $(Y, d)$도 metric space.</li>
<li>이유: 정의 2.15의 조건들이 $Y$ 위에서도 그대로 유지되기 때문.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="✅-217-definition-구간-볼-볼록-집합">✅ 2.17 Definition (구간, 볼, 볼록 집합)</h2>
<h3 id="1-구간의-정의">1. 구간의 정의</h3>
<ul>
<li><strong>열린 구간 (open interval)</strong>:  
$(a, b) = { x \in \mathbb{R} \mid a &lt; x &lt; b }$</li>
<li><strong>닫힌 구간 (closed interval)</strong>:  
$[a, b] = { x \in \mathbb{R} \mid a \le x \le b }$</li>
<li><strong>반열린 구간 (half-open interval)</strong>:  
$[a, b) = { x \in \mathbb{R} \mid a \le x &lt; b }$  
$(a, b] = { x \in \mathbb{R} \mid a &lt; x \le b }$</li>
</ul>
<hr>
<h3 id="2-k-cell">2. k-cell</h3>
<ul>
<li>$a_i &lt; b_i$ for $i = 1, \dots, k$일 때,<br>$\mathbb{R}^k$의 모든 점 $x = (x_1, \dots, x_k)$ 중<br>$a_i \le x_i \le b_i$를 만족하는 점들의 집합:
$$
[a_1, b_1] \times [a_2, b_2] \times \dots \times [a_k, b_k]
$$</li>
<li>예:<ul>
<li>1-cell: $[a, b]$ (구간)</li>
<li>2-cell: 직사각형</li>
<li>3-cell: 직육면체</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-open-ball--closed-ball">3. Open Ball / Closed Ball</h3>
<ul>
<li><p>중심 $x \in \mathbb{R}^k$, 반지름 $r &gt; 0$일 때:</p>
<ul>
<li><p><strong>Open ball</strong>:  
$B(x, r) = { y \in \mathbb{R}^k \mid |y - x| &lt; r }$</p>
</li>
<li><p><strong>Closed ball</strong>:  
$\overline{B}(x, r) = { y \in \mathbb{R}^k \mid |y - x| \le r }$</p>
</li>
</ul>
</li>
<li><p>$\mathbb{R}^2$에서는 각각 원의 내부, 원 내부+테두리에 해당.</p>
</li>
</ul>
<hr>
<h3 id="4-convex-set-볼록-집합">4. Convex Set (볼록 집합)</h3>
<ul>
<li><p>집합 $E \subset \mathbb{R}^k$가 다음 조건을 만족하면 convex라고 한다:</p>
<blockquote>
<p>임의의 $x, y \in E$ 및 $0 &lt; \lambda &lt; 1$에 대해<br>$\lambda x + (1 - \lambda)y \in E$</p>
</blockquote>
</li>
<li><p><strong>직관</strong>: 두 점을 이은 선분 전체가 집합 안에 들어갈 때, 그 집합은 convex.
<img src="https://velog.velcdn.com/images/g1fted_13/post/ee4a93da-4133-4b90-bbc1-4dbe5d98da5b/image.jpeg" alt=""></p>
</li>
</ul>
<hr>
<h4 id="예시-ball은-convex">예시: Ball은 Convex</h4>
<p>볼 $B(x, r)$ 안에 두 점 $y$, $z$가 있다고 하자. 즉,</p>
<p>$$
|y - x| &lt; r, \quad |z - x| &lt; r
$$</p>
<p>이때, 선분 위의 점 $w = \lambda y + (1 - \lambda)z$ 가 여전히 ball 안에 들어감을 보이자.</p>
<p><strong>📝 Proof</strong></p>
<ol>
<li><p>선분 위의 점은 다음과 같이 정의된다:</p>
<p>$$
w = \lambda y + (1 - \lambda)z
$$</p>
</li>
<li><p>$w$가 ball 안에 있는지를 확인하기 위해 다음을 계산한다:</p>
<p>$$
|w - x| = |\lambda y + (1 - \lambda)z - x|
$$</p>
</li>
<li><p>벡터 항을 정리하면:</p>
<p>$$
|w - x| = |\lambda(y - x) + (1 - \lambda)(z - x)|
$$</p>
</li>
<li><p>삼각부등식을 적용하면:</p>
<p>$$
|w - x| \le \lambda |y - x| + (1 - \lambda)|z - x|
$$</p>
</li>
<li><p>$|y - x| &lt; r$, $|z - x| &lt; r$ 이므로:</p>
<p>$$
\lambda |y - x| + (1 - \lambda)|z - x| &lt; \lambda r + (1 - \lambda)r = r
$$</p>
</li>
<li><p>따라서,</p>
<p>$$
|w - x| &lt; r \Rightarrow w \in B(x, r)
$$</p>
</li>
</ol>
<p>→ 결론적으로, open ball은 convex이다.</p>
<hr>
<ul>
<li>같은 논리로 <strong>closed ball</strong>도 convex임을 증명할 수 있다 (등호 허용).</li>
<li><strong>$k$-cell</strong> (직사각형, 직육면체 등) 역시 convex이다.</li>
<li>반면, <strong>오목한 모양이나 구멍 뚫린 집합</strong>은 convex가 아니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.2 (2.9~2.14)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.92.14</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.92.14</guid>
            <pubDate>Fri, 16 May 2025 17:05:55 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-29-definition-집합의-모음-collection-of-sets">✅ 2.9 Definition (집합의 모음, Collection of Sets)</h2>
<ul>
<li>집합 $A$의 각 원소 $\alpha$에 대해 집합 $\Omega$의 부분집합 $E_\alpha$가 대응된다고 하자.</li>
<li>이때 $E_\alpha$  를 원소로 갖는 집합 ${E_\alpha}$는 <strong>집합의 집합(set of sets)</strong> 이다.</li>
<li>이러한 경우 <strong>&#39;집합의 모음(collection of sets)&#39; 또는 &#39;집합의 족(family of sets)&#39;</strong> 이라고 부른다.</li>
<li><strong>즉, set of sets 대신 collection이나 family라는 표현을 사용하여 보다 간결하게 표현할 수 있다.</strong></li>
</ul>
<hr>
<h2 id="✅-212-theorem-countable-set의-union도-countable">✅ 2.12 Theorem (Countable Set의 Union도 Countable)</h2>
<p>Countable sets(가산 집합들)의 수열 ${E_n}$ ($n=1,2,3,\dots$)이 주어졌을 때,
$$
S = \bigcup_{n=1}^\infty E_n
$$
은 가산 집합이다.</p>
<h4 id="📝-proof-증명">📝 Proof (증명)</h4>
<ul>
<li>각 $E_n$의 원소들을 수열 ${x_{nk}}$ ($k=1,2,3,\dots$)로 나열한다.</li>
<li>이들을 2차원 배열로 정리하면:
$$
\begin{matrix}
x_{11} &amp; x_{12} &amp; x_{13} &amp; \dots \
x_{21} &amp; x_{22} &amp; x_{23} &amp; \dots \
x_{31} &amp; x_{32} &amp; x_{33} &amp; \dots \
\vdots &amp; \vdots &amp; \vdots &amp; \ddots
\end{matrix}
$$</li>
<li>위 배열을 <strong>대각선 순서(diagonal order)</strong> 로 나열하면:
$$
x_{11}, x_{21}, x_{12}, x_{31}, x_{22}, x_{13}, \dots
$$</li>
<li>이 나열은 $S$의 모든 원소를 포함한다.</li>
<li>중복 원소가 있다면 제거해도 여전히 $S$는 Countable Set(Theorem 2.8 -&gt; Every infinite subset of a countable set is countable).</li>
</ul>
<hr>
<h3 id="📌-corollary">📌 Corollary</h3>
<ul>
<li>$A$가 <strong>at least countable (가산 이하 집합)</strong> 이고,<br>$A$의 각 원소 $\alpha$마다 집합 $B_\alpha$가 대응되며,<br>이 $B_\alpha$들도 모두 <strong>가산 이하 집합</strong>이라고 하자.</li>
<li>이때 다음과 같은 <strong>합집합 $T$</strong> 를 생각한다:
$$
T = \bigcup_{\alpha \in A} B_\alpha
$$</li>
<li>즉, $A$의 각 $\alpha$가 갖고 있는 $B_\alpha$들을 모두 모은 집합 $T$이다.</li>
<li><strong>결론</strong>: $T$ 역시 <strong>가산 이하 집합</strong>이다.</li>
</ul>
<hr>
<h2 id="✅-📌-213-theorem-countable-set의-원소로-이뤄진-n-tuple의-set도-countable">✅ 📌 2.13 Theorem (Countable Set의 원소로 이뤄진 n-tuple의 set도 Countable)</h2>
<p>Countable Set(가산 집합) $A$에 대해, $A$의 원소들로 이루어진 $n$-tuple들의 집합 $B_n$을 다음과 같이 정의한다:
$$
B_n = { (a_1, \dots, a_n) \mid a_k \in A \text{ for } k = 1, \dots, n }
$$
여기서 $a_1, \dots, a_n$은 서로 달라도 되고 같아도 된다.
이때 $B_n$ 은 Countable이다.</p>
<h4 id="📝-proof-증명-귀납법-사용">📝 Proof (증명: 귀납법 사용)</h4>
<ol>
<li>$B_1$은 $A$ 자체이므로 countable이다.</li>
<li>$B_n$의 원소는 다음과 같은 형태로 쓸 수 있다:
$$
(b, a) \text{ where } b \in B_{n-1}, a \in A
$$</li>
<li>$B_{n-1}$가 countable이고, $A$도 countable이므로, 고정된 $b$에 대해 $(b, a)$의 집합은 $A$와 equivalent(동치)이며 countable이다.</li>
<li>따라서 $B_n$은 <strong>union of a countable set of countable sets(가산 개수의 가산 집합들의 합집합)</strong> 이다.</li>
<li>Theorem 2.12에 의해 $B_n$은 가산 집합이다.</li>
<li>귀납법으로 $B_n$이 모든 $n$에 대해 가산임이 성립.</li>
</ol>
<hr>
<h3 id="📌-corollary-유리수-집합의-가산성">📌 Corollary (유리수 집합의 가산성)</h3>
<blockquote>
<p><strong>모든 유리수들의 집합 $\mathbb{Q}$는 Countable(가산)이다.</strong></p>
</blockquote>
<h4 id="이유">이유</h4>
<ul>
<li>모든 유리수 $p$는 $\frac{b}{a}$ 의 형태로 나타낼 수 있으며, $a, b$는 정수이다.</li>
<li>정수들의 쌍 $(a, b)$의 집합은 $B_2$로 볼 수 있고, 이는 Theorem 2.13에 의해 가산이다.</li>
<li>따라서 유리수들의 집합도 가산이다.
<img src="https://velog.velcdn.com/images/g1fted_13/post/b12c9285-50ae-493f-9f5d-0abac913b8f7/image.jpeg" alt=""></li>
</ul>
<hr>
<h2 id="✅-214-theorem-0과-1로-이뤄진-무한-수열-집합은-uncountable">✅ 2.14 Theorem (0과 1로 이뤄진 무한 수열 집합은 uncountable)</h2>
<blockquote>
<p><strong>0과 1로 이루어진 모든 무한 수열들의 집합 $A$는 uncountable(비가산)이다.</strong> </p>
</blockquote>
<h4 id="📝-proof-칸토어의-대각선-논법">📝 Proof (칸토어의 대각선 논법)</h4>
<ol>
<li>$A$가 countable이라고 가정하자.</li>
<li>그러면 $A$의 원소들을 다음과 같이 나열할 수 있다:
$$
s_1, s_2, s_3, \dots
$$</li>
<li>새로운 수열 $s$를 다음과 같이 만든다:
$$
\text{만약 } s_n \text{의 } n\text{번째 자릿수가 } 1 \text{이면 } s \text{의 } n\text{번째 자릿수는 } 0, \
\text{만약 } s_n \text{의 } n\text{번째 자릿수가 } 0 \text{이면 } s \text{의 } n\text{번째 자릿수는 } 1.
$$</li>
<li>이렇게 만든 $s$는 $s_1, s_2, s_3, \dots$ 중 어느 것과도 적어도 한 자릿수 이상 다르다.</li>
<li>그러나 $s$는 분명 $A$의 원소이므로, $s$는 $A$에 있어야 한다.</li>
<li>이는 $A$가 countable이라는 가정과 모순이다.</li>
<li>따라서 $A$는 uncountable이다.</li>
</ol>
<hr>
<h4 id="💡-참고-cantors-diagonal-process">💡 참고: Cantor&#39;s Diagonal Process</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/741f094d-24d2-4c1b-907e-6dc6b413a3af/image.jpeg" alt=""></p>
<ul>
<li>이 증명 방식은 <strong>칸토어의 대각선 논법(Cantor&#39;s diagonal argument)</strong> 이라고 부른다.</li>
<li>0과 1로 이루어진 무한 수열을 <strong>실수의 이진 표현(binary representation)</strong> 으로 해석하면,<br><strong>실수 전체 집합이 uncountable</strong>이라는 사실이 바로 따라옴을 알 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.2 (2.1~2.8)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.1</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.2-2.1</guid>
            <pubDate>Thu, 15 May 2025 14:49:30 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-21-function-함수">✅ 2.1 Function (함수)</h2>
<ul>
<li><strong>두 집합 $A$, $B$가 주어졌을 때, $A$의 각 원소 $x$에 대해 $B$의 원소 $f(x)$가 어떤 방식으로든 대응되면, $f$는 $A$에서 $B$로의 함수(function) 또는 사상(mapping)</strong> 이라고 한다.</li>
<li><strong>$f$의 정의역(domain)</strong>: $A$</li>
<li><strong>$f$의 값(value)</strong>: $f(x)$</li>
<li><strong>$f$의 치역(range)</strong>: $f(A)$ (모든 $f(x)$의 집합) (<strong>집합 $A$에 대해서도 함수 $f$를 직접 적용하여 표현할 수 있다!</strong>)</li>
</ul>
<hr>
<h2 id="✅-22-onto전사--one-to-one단사">✅ 2.2 Onto(전사) / One-to-One(단사)</h2>
<h4 id="image-상-이미지">Image (상, 이미지)</h4>
<ul>
<li>$f: A \to B$가 주어졌을 때, $E \subset A$라면<ul>
<li>$f(E) = {f(x) \mid x \in E}$ 를 <strong>$E$의 $f$에 의한 상(image)</strong> 이라고 한다.</li>
<li>이때 $f(A)$는 $f$의 치역(range)이며 항상 $f(A) \subset B$이다.</li>
<li>만약 $f(A) = B$이면, <strong>$f$는 $A$를 $B$로 &#39;onto&#39; (전사)</strong> 한다고 한다.</li>
</ul>
</li>
</ul>
<h4 id="inverse-image-역상">Inverse Image (역상)</h4>
<ul>
<li>$E \subset B$라면<ul>
<li>$f^{-1}(E) = {x \in A \mid f(x) \in E}$ 를 <strong>$E$의 $f$에 의한 원상(inverse image)</strong> 이라고 한다.</li>
<li><strong>주의</strong>: 이는 $f$가 역함수를 가지는지와 무관하게 정의된다.</li>
</ul>
</li>
</ul>
<h4 id="one-to-one-mapping-단사">One-to-One Mapping (단사)</h4>
<ul>
<li>모든 $y \in B$에 대해 $f^{-1}(y)$가 $A$의 원소를 <strong>최대 하나만 포함</strong>하면 $f$는 <strong>일대일(one-to-one, 1-1) mapping</strong> 이라고 한다.</li>
<li>즉, $f(x_1) \ne f(x_2)$ whenever $x_1 \ne x_2$ ($x_1, x_2 \in A$).</li>
</ul>
<hr>
<h2 id="✅-23-equivalence-of-sets-집합의-동치">✅ 2.3 Equivalence of Sets (집합의 동치)</h2>
<ul>
<li><strong>$A$에서 $B$로의 일대일 대응(one-to-one mapping)이 존재한다면, $A$와 $B$는 1-1 &#39;correspondence&#39;가 있다고 하며, 같은 &#39;cardinal number&#39;(기수)를 가진다고 한다.</strong></li>
<li>이를 간단히 <strong>$A \sim B$</strong> 라고 쓴다.</li>
<li>이 관계는 다음 성질들을 만족한다:<ul>
<li><strong>Reflexive (반사성)</strong>: $A \sim A$</li>
<li><strong>Symmetric (대칭성)</strong>: $A \sim B$ 이면 $B \sim A$</li>
<li><strong>Transitive (추이성)</strong>: $A \sim B$이고 $B \sim C$이면 $A \sim C$</li>
</ul>
</li>
<li>이러한 성질을 갖는 관계를 <strong>equivalence relation (동치 관계)</strong> 이라고 한다.</li>
</ul>
<hr>
<h2 id="✅-24-cardinality-기수">✅ 2.4 Cardinality (기수)</h2>
<ul>
<li>$n$이 양의 정수일 때, <strong>$J_n = {1, 2, \dots, n}$</strong>  </li>
<li><strong>$J$는 모든 양의 정수(=자연수)의 집합 ${1, 2, 3, \dots}$</strong></li>
<li>임의의 집합 $A$에 대해 다음과 같이 정의한다:<ul>
<li><strong>a) Finite (유한 집합)</strong>: $A \sim J_n$인 $n$이 존재 (공집합도 유한으로 간주)</li>
<li><strong>b) Infinite (무한 집합)</strong>: $A$가 유한이 아닐 때</li>
<li><strong>c) Countable (가산 집합)</strong>: $A \sim J$</li>
<li><strong>d) Uncountable (비가산 집합)</strong>: 유한도 아니고 가산도 아닐 때</li>
<li><strong>e) At most countable (가산 이하 집합)</strong>: 유한 또는 가산일 때  </li>
</ul>
</li>
<li>💡 <strong>Countable set은 enumerable(열거 가능), denumerable(가산 가능)이라고도 불린다.</strong></li>
</ul>
<hr>
<h2 id="✅-25-example-정수-집합은-가산이다">✅ 2.5 Example (정수 집합은 가산이다)</h2>
<ul>
<li><strong>$A$ = 모든 정수들의 집합 ${0, 1, -1, 2, -2, 3, -3, \dots}$</strong>  </li>
<li><strong>$J$ = ${1, 2, 3, 4, 5, \dots}$</strong></li>
<li>$f: J \to A$인 함수를 다음과 같이 명시적으로 정의함으로써, $J$와 $A$가 동치임을 확인할 수 있다 :<ul>
<li>$f(n) = \begin{cases} \frac{n}{2}, &amp; \text{if } n \text{ is even} \ -\frac{n-1}{2}, &amp; \text{if } n \text{ is odd} \end{cases}$</li>
</ul>
</li>
<li>💡 <strong>따라서 $A$는 가산(countable)이다.</strong></li>
</ul>
<hr>
<h2 id="✅-26-remark-무한-집합의-특성">✅ 2.6 Remark (무한 집합의 특성)</h2>
<ul>
<li><p>유한 집합은 자신의 proper subset(진부분집합)과 동치가 될 수 없다.</p>
</li>
<li><p>그러나 <strong>무한 집합은 자신의 진부분집합과 동치가 가능</strong>하다.</p>
<ul>
<li>ex) Example 2.5에서 <strong>$J$는 $A$의 진부분집합이지만 $A \sim J$</strong></li>
</ul>
</li>
<li><p>💡 따라서 <strong>Def 2.4(b)</strong> 에서 무한 집합의 정의는 다음과 같이 바꿀 수 있다:</p>
<ul>
<li><strong>$A$가 무한이라는 것은 $A$가 자신의 진부분집합과 동치인 경우이다.</strong></li>
</ul>
<hr>
<h2 id="✅-26-remark-무한-집합의-특성-1">✅ 2.6 Remark (무한 집합의 특성)</h2>
</li>
<li><p><strong>수열(sequence)</strong> 이란, <strong>모든 양의 정수들의 집합 $J$ 위에서 정의된 함수 $f$를 의미</strong>한다.</p>
</li>
<li><p>$f(n) = x_n$ ($n \in J$) 일 때, 수열 $f$는 보통 <strong>${x_n}$</strong> 또는 $x_1, x_2, x_3, \dots$ 로 표기한다.</p>
</li>
<li><p>$f$의 값(value), 즉 <strong>$x_n$ 들을 수열의 항(term)</strong> 이라고 한다.</p>
</li>
<li><p>만약 $A$가 어떤 집합이고, 모든 $n \in J$에 대해 $x_n \in A$라면, ${x_n}$를 <strong>$A$ 안의 수열(sequence in $A$)</strong> 또는 <strong>$A$의 원소들로 이루어진 수열</strong> 이라고 한다.</p>
</li>
</ul>
<hr>
<h4 id="📌-중요-포인트-countable-set과-수열의-연결">📌 중요 포인트 (Countable set과 수열의 연결)</h4>
<ul>
<li><strong>모든 countable set(가산집합)은 $J$ 위에 정의된 일대일 함수의 range(치역)으로 표현 가능</strong>하다.<ul>
<li>즉, <strong>Countable set의 원소들은 &#39;서열화(arranged in a sequence)&#39; 될 수 있다.</strong></li>
</ul>
</li>
<li>💡 쉽게 얘기해서, <strong>Countable set의 원소들을 수열로 나열할 수 있다</strong> 라고 말할 수 있다.</li>
</ul>
<hr>
<h4 id="✅-참고">✅ 참고</h4>
<ul>
<li>때로는 편의를 위해 $J$ 대신 <strong>$0$ 이상의 정수들의 집합 ${0, 1, 2, \dots}$</strong> 을 사용하는 경우도 있다.<ul>
<li>이 경우 <strong>수열을 $x_0, x_1, x_2, \dots$ 로 시작</strong>한다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="✅-28-theorem-가산-집합의-부분집합">✅ 2.8 Theorem (가산 집합의 부분집합)</h2>
<blockquote>
<p><strong>Countable Set(가산집합) $A$의 모든 Infinite Subset(무한 부분집합) $E$는 Countable(가산)이다.</strong></p>
</blockquote>
<hr>
<h4 id="📝-proof-증명">📝 Proof (증명)</h4>
<ul>
<li>$A$가 Countable Set이고 $E \subset A$, $E$가 Infinite Set이라고 하자.</li>
<li>$A$의 원소들을 서로 다른 항으로 나열한 수열 ${x_n}$를 생각한다.</li>
<li>이제 $E$의 원소들을 다음과 같이 ${x_n}$에서 순서대로 찾아 수열 ${n_k}$를 구성한다:<ol>
<li>$n_1$은 $x_{n_1} \in E$인 가장 작은 $n$.</li>
<li>$n_2$는 $n_1$보다 큰 수 중 $x_{n_2} \in E$인 가장 작은 $n$.</li>
<li>일반적으로, $n_k$는 $n_{k-1}$보다 큰 수 중 $x_{n_k} \in E$인 가장 작은 $n$.</li>
</ol>
</li>
<li>이렇게 하면 ${f(k) = x_{n_k}}$ ($k=1,2,3,\dots$)는 $E$의 서로 다른 모든 원소를 포함하는 <strong>수열</strong>이 되며,<ul>
<li>이는 <strong>$E$와 $J$ 사이의 일대일 대응(one-to-one correspondence)</strong> 을 만들어준다.</li>
</ul>
</li>
</ul>
<hr>
<h4 id="💡-직관적-해석-중요">💡 직관적 해석 (중요!!)</h4>
<ul>
<li><strong>Countable Set(가산집합)은 &#39;가장 작은&#39; Infinite Set(무한집합)이다.</strong></li>
<li><strong>Uncountable Set(비가산 집합)은 절대 Countable Set(가산집합)의 subset(부분집합)이 될 수 없다.</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.1 (1.19~Appendix)]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.1-1.19Appendix</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.1-1.19Appendix</guid>
            <pubDate>Mon, 05 May 2025 10:35:27 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-119-the-real-field">✅ 1.19 The Real Field</h2>
<ul>
<li>체(Field): 두 연산(덧셈, 곱셈)이 정의된 집합 $F$로서, 각각에 대해 항등원, 역원, 교환법칙, 결합법칙, 분배법칙이 모두 성립한다.</li>
<li>순서체(Ordered field): 체 $F$에 &quot;&lt;&quot;라는 순서 관계가 추가되어, 크기 비교가 가능함.</li>
</ul>
<h3 id="🔸-theorem">🔸 Theorem</h3>
<blockquote>
<p>There exists an ordered field $\mathbb{R}$ with the least-upper-bound property. Moreover, $\mathbb{Q} \subset \mathbb{R}$.</p>
</blockquote>
<p>이 체 $\mathbb{R}$의 원소들을 <strong>실수(real numbers)</strong>라 부른다.</p>
<ul>
<li>이 정리는 우리가 해석학을 전개할 수 있는 완비 공간 $\mathbb{R}$의 존재를 보장한다.</li>
<li>$\mathbb{R}$의 구성은 Appendix에서 다룬다 (Dedekind cut).</li>
</ul>
<hr>
<h2 id="✅-120-archimedean-property--density-of-mathbbq">✅ 1.20 Archimedean Property &amp; Density of $\mathbb{Q}$</h2>
<h3 id="🔸-thm-a---archimedean-property">🔸 Thm (a) - Archimedean Property</h3>
<blockquote>
<p>If $x &gt; 0$ and $y \in \mathbb{R}$, then there exists a positive integer $n$ such that $nx &gt; y$.</p>
</blockquote>
<h4 id="✏️-proof">✏️ Proof</h4>
<ol>
<li><p><strong>Proof by contradiction</strong>: 모든 정수 $n \in \mathbb{N}$에 대해 $nx \leq y$ 가정.</p>
</li>
<li><p>그러면 집합 $A = { nx \mid n \in \mathbb{N} }$는 <strong>upper bounded(위로 유계)</strong> 이다.</p>
</li>
<li><p>따라서 $\alpha = \sup A$ 가 존재함 ($\mathbb{R}$의 least-upper-bound property에 의해)</p>
</li>
<li><p>그런데 $x &gt; 0$이므로 $\alpha - x &lt; \alpha$</p>
</li>
<li><p>$\alpha$는 supremum(상한)이므로, $\alpha - x$는 upper bound(상계)가 아님. 
즉, 어떤 $n \in \mathbb{N}$에 대해
$$
nx &gt; \alpha - x
$$</p>
</li>
<li><p>따라서
$$
(n + 1)x &gt; \alpha
$$</p>
<p>하지만 $(n + 1)x \in A$이고, $\alpha$는 $A$의 상한인데, 이를 초과하는 원소가 존재한다는 것은 <strong>모순</strong></p>
</li>
</ol>
<p>$\therefore$ 가정이 잘못되었으므로,   <strong>$nx &gt; y$를 만족하는 어떤 $n \in \mathbb{N}$ 가 항상 존재한다 .</strong> $\quad \blacksquare$</p>
<h3 id="🔸-thm-b---mathbbq-is-dense-in-mathbbr">🔸 Thm (b) - $\mathbb{Q}$ is dense in $\mathbb{R}$</h3>
<blockquote>
<p>If $x &lt; y$, then there exists a rational number $p$ such that $x &lt; p &lt; y$.</p>
</blockquote>
<h4 id="✏️-proof-1">✏️ Proof</h4>
<p>$y - x &gt; 0$이므로, Archimedean 성질에 의해 $n \in \mathbb{N}$이 존재하여 $n(y - x) &gt; 1$.<br>즉, $nx &lt; ny - 1$, 이 사이에는 정수 $m$이 존재하므로 $nx &lt; m &lt; ny$.<br>양변을 $n$으로 나누면 $x &lt; \frac{m}{n} &lt; y$, 이때 $\frac{m}{n} \in \mathbb{Q}$.<br>→ 유리수는 실수 사이에 항상 존재한다. $\quad \blacksquare$</p>
<hr>
<h2 id="✅-121-n제곱근의-존재와-유일성">✅ 1.21 $n$제곱근의 존재와 유일성</h2>
<h3 id="🔸-theorem-1">🔸 Theorem</h3>
<blockquote>
<p>For every real number $x &gt; 0$ and every integer $n &gt; 0$, there exists a unique real number $y &gt; 0$ such that $y^n = x$.</p>
</blockquote>
<h4 id="✏️-proof-existence">✏️ Proof (Existence)</h4>
<ol>
<li><p>$A = { t  \mid 0 &lt; t^n &lt; x }$ 라는 집합을 생각하자.</p>
</li>
<li><p>$A$는 공집합이 아님<br>→ $x &gt; 0$이므로 $t = \frac{x}{2}$ 같은 값은 $t^n &lt; x$를 만족</p>
</li>
<li><p>$A$는 위로 유계<br>→ 예를 들어 $x + 1$ 같은 값은 $(x+1)^n &gt; x$이므로 $A$의 상계</p>
</li>
<li><p>따라서 $A \subset \mathbb{R}$는 위로 유계인 실수 부분집합<br>→ $\mathbb{R}$은 least-upper-bound property를 가지므로:</p>
<p>$$
y = \sup A
$$</p>
<p>이 존재함</p>
</li>
<li><p>이제 $y^n = x$임을 보이자.</p>
<ul>
<li>만약 $y^n &lt; x$이면, $y + \epsilon \in A$가 되어 $y$보다 큰 상한이 생김 → 모순  </li>
<li>만약 $y^n &gt; x$이면, $y - \epsilon$은 $A$의 상계가 되는데 $y$보다 작음 → 모순  </li>
</ul>
</li>
</ol>
<p>$\Rightarrow$ 따라서 $y^n = x$</p>
<h4 id="✏️-proof-uniqueness">✏️ Proof (Uniqueness)</h4>
<p>함수 $f(t) = t^n$은 $t &gt; 0$에서 단조 증가하므로, $f(t_1) = f(t_2)$이면 $t_1 = t_2$이다.<br>→ 따라서 해는 유일하다. $\quad \blacksquare$</p>
<hr>
<h3 id="🔸-corollary">🔸 Corollary</h3>
<blockquote>
<p>If $a &gt; 0$, $b &gt; 0$ and $n \in \mathbb{N}$, then
$$
(ab)^{1/n} = a^{1/n} \cdot b^{1/n}
$$</p>
</blockquote>
<h4 id="✏️-proof-sketch">✏️ Proof (Sketch)</h4>
<p>양변을 $n$제곱하면:
$$
((ab)^{1/n})^n = ab = a \cdot b = (a^{1/n})^n \cdot (b^{1/n})^n = (a^{1/n} \cdot b^{1/n})^n
$$</p>
<p>→ 양변의 $n$제곱이 같고 양수이므로 원래 수들도 같다. $\quad \blacksquare$</p>
<hr>
<h2 id="✅-정리-요약">✅ 정리 요약</h2>
<ul>
<li>$\mathbb{R}$은 순서체이며, least-upper-bound property를 만족하는 유일한 체이다.</li>
<li>Archimedean 성질은 $\mathbb{R}$의 수들이 무한히 커질 수 있음을 보장한다.</li>
<li>유리수는 실수 사이에 항상 존재한다 → $\mathbb{Q}$는 $\mathbb{R}$에서 조밀(dense)하다.</li>
<li>실수는 모든 양의 수에 대해 $n$제곱근을 갖고, 이는 유일하다.</li>
</ul>
<hr>
<h2 id="✅-136-euclidean-spaces">✅ 1.36 Euclidean Spaces</h2>
<p>양의 정수 $k$에 대해, $\mathbb{R}^k$는 다음과 같은 모든 <strong>$k$-tuple</strong>의 집합이다:</p>
<p>$$
x = (x_1, x_2, \dots, x_k), \quad \text{where each } x_i \in \mathbb{R}
$$</p>
<p>이때, 각 $x_i$는 <strong>좌표(coordinate)</strong> 라고 하며,<br>$\mathbb{R}^k$의 원소 $x$는 보통 <strong>점(point)</strong> 또는 <strong>벡터(vector)</strong> 라고 부른다.<br>($k = 1$일 때는 실수 직선, $k = 2$는 평면, $k = 3$은 3차원 공간을 의미함)</p>
<hr>
<h3 id="🔸-inner-product-내적">🔸 Inner Product (내적)</h3>
<p>임의의 $x = (x_1, ..., x_k)$, $y = (y_1, ..., y_k) \in \mathbb{R}^k$에 대해<br><strong>내적(inner product)</strong> 은 다음과 같이 정의한다:</p>
<p>$$
x \cdot y = \sum_{i=1}^{k} x_i y_i = x_1 y_1 + x_2 y_2 + \dots + x_k y_k
$$</p>
<hr>
<h3 id="🔸-norm-벡터의-크기">🔸 Norm (벡터의 크기)</h3>
<p>벡터 $x \in \mathbb{R}^k$의 <strong>노름(norm)</strong>, 또는 <strong>크기(magnitude)</strong> 는 다음과 같다:</p>
<p>$$
|x| = \sqrt{x \cdot x} = \sqrt{x_1^2 + x_2^2 + \dots + x_k^2}
$$</p>
<hr>
<h2 id="✅-137-thm-노름과-내적의-기본-성질">✅ 1.37 Thm: 노름과 내적의 기본 성질</h2>
<p>임의의 $x, y, z \in \mathbb{R}^k$, 스칼라 $\alpha \in \mathbb{R}$에 대해 다음이 성립한다:</p>
<ul>
<li>$\quad |x| \geq 0$</li>
<li>$\quad |x| = 0 \iff x = 0$</li>
<li>$\quad |\alpha x| = |\alpha||x|$</li>
<li>$\quad |x \cdot y| \leq |x||y|$ (Cauchy-Schwarz 부등식)</li>
<li>$\quad |x + y| \leq |x| + |y|$ (삼각부등식)</li>
<li>$\quad |x - z| \leq |x - y| + |y - z|$ (거리의 삼각부등식)</li>
</ul>
<hr>
<h2 id="✅-138-rmk-mathbbrk는-metric-space거리-공간">✅ 1.38 Rmk: $\mathbb{R}^k$는 metric space(거리 공간)</h2>
<p>위 정리 1.37의 결과 중 (a), (b), (f)는<br>$\mathbb{R}^k$가 <strong>metric space (거리공간)</strong> 으로 다뤄질 수 있음을 보장한다.</p>
<h4 id="💡--metric-space-거리공간">💡  <strong>metric space (거리공간)</strong></h4>
<p><strong>&quot;점들 사이의 거리(distance)&quot;</strong> 를 정의할 수 있는 공간</p>
<ul>
<li>두 점 $x, y \in \mathbb{R}^k$ 사이의 거리 $d(x, y)$를 다음과 같이 정의:</li>
</ul>
<p>$$
d(x, y) := |x - y|
$$</p>
<hr>
<h2 id="✅-appendix">✅ Appendix</h2>
<p>유리수 집합($\mathbb{Q}$)에서 실수 집합($\mathbb{R}$)을 구성하는 Dedekind 절단(Dedekind Cut) 방식을 정리한다.</p>
<hr>
<h3 id="📌-step-1-dedekind-cut의-정의">📌 Step 1: Dedekind Cut의 정의</h3>
<p><strong>Dedekind 절단(Dedekind Cut)</strong> 이란, 유리수 집합 $\mathbb{Q}$의 부분집합 $\alpha$로 다음 세 조건을 만족하는 것을 말한다.</p>
<ol>
<li>$\alpha \ne \emptyset$, $\alpha \ne \mathbb{Q}$</li>
<li>만약 $p \in \alpha$이고 $q &lt; p$이면, $q \in \alpha$ </li>
<li>$\alpha$는 최대 원소를 갖지 않는다.</li>
</ol>
<p>직관적으로 생각하면 cut은 유리수로 이뤄진 오른쪽이 열린 반직선으로 생각해볼 수 있다.
이후 <strong>$\mathbb{Q}$의 모든 cut의 집합을 $\mathbb{R}$</strong> 로 표기한다.</p>
<hr>
<h3 id="📌-step-2-mathbbr의-순서-관계-정의">📌 Step 2: $\mathbb{R}$의 순서 관계 정의</h3>
<p>두 cut  $\alpha, \beta \in \mathbb{R}$에 대해 다음과 같이 순서 관계를 정의한다.</p>
<p>$$
\alpha &lt; \beta \quad\Longleftrightarrow\quad \alpha \text{ is a proper subset of } \beta
$$</p>
<p>이 순서는 다음 성질을 만족하여, $\mathbb{R}$을 ordered set(순서집합) 으로 만든다.</p>
<ul>
<li>$\alpha &lt; \beta$이고 $\beta &lt; \gamma$이면 $\alpha  &lt; \gamma$</li>
<li>$\alpha &lt; \beta$, $\alpha = \beta$, $\beta &lt; \alpha$ 중 오직 하나만 성립</li>
</ul>
<hr>
<h3 id="📌-step-3-least-upper-bound-property">📌 Step 3: Least Upper Bound Property</h3>
<p>$\mathbb{R}$의 임의의 공집합이 아닌 부분집합 $S$가 bounded above(위로 유계)라고 하자. 그러면 다음을 보일 수 있다.</p>
<ul>
<li>집합의 supremum(상한) 
$\sup S = \bigcup S$는 역시 cut이 된다.</li>
<li>즉, $\mathbb{R}$은 Least-upper-bound property를 가지는 집합이다.</li>
</ul>
<p>이 성질을 통해 $\mathbb{R}$은 완비성을 갖게 된다.</p>
<hr>
<h3 id="📌-step-4-유리수의-포함-관계">📌 Step 4: 유리수의 포함 관계</h3>
<p>임의의 유리수 $r \in \mathbb{Q}$에 대해 다음과 같은 Dedekind 절단을 정의하자.</p>
<p>$$
r^* = { q \in \mathbb{Q} \mid q &lt; r }
$$</p>
<p>이때, 유리수 $r$을 이렇게 구성된 절단으로 표현하면,<br>$\mathbb{Q}$는 자연스럽게 $\mathbb{R}$의 부분집합으로 포함된다.</p>
<h4 id="💡-보충-설명">💡 보충 설명</h4>
<ol>
<li><p><strong>$\mathbb{Q} \cong \mathbb{Q}^*$</strong><br>(유리수 $\mathbb{Q}$와 절단으로 표현된 집합 $\mathbb{Q}^<em>$는 순서체로서 동형이다)<br>⇒ 덧셈, 곱셈, 순서 관계까지 *</em>보존하는 isomorphism**이 존재한다.</p>
</li>
<li><p><strong>$\mathbb{Q}^* \subset \mathbb{R}$</strong><br>⇒ 각 $r^* \in \mathbb{Q}^*$는 Dedekind cut이므로 실수 $\mathbb{R}$의 원소이다.</p>
</li>
<li><p><strong>$\therefore\ \mathbb{Q} \cong \mathbb{Q}^* \subset \mathbb{R}$</strong><br>⇒ $\mathbb{Q}$는 $\mathbb{R}$ 안에 <strong>자연스럽게 포함(embedding)</strong>되며,<br>이 포함은 단순한 집합 이론적 포함이 아니라 <strong>구조(덧셈, 곱셈, 순서)를 보존하는 포함</strong>이다.</p>
</li>
</ol>
<hr>
<h3 id="📌-step-5-덧셈의-정의">📌 Step 5: 덧셈의 정의</h3>
<p>두 실수 $\alpha, \beta \in \mathbb{R}$에 대한 덧셈은 다음과 같이 정의한다.</p>
<p>$$
\alpha + \beta = { r + s \mid r \in \alpha,, s \in \beta }
$$</p>
<p>이 덧셈은 다음 성질을 만족한다.</p>
<ul>
<li>닫혀있음(closed)</li>
<li>결합법칙(associativity), 교환법칙(commutativity)을 만족</li>
</ul>
<hr>
<h3 id="📌-step-6-곱셈의-정의">📌 Step 6: 곱셈의 정의</h3>
<p>두 양의 실수 $\alpha, \beta &gt; 0$에 대해서 곱셈을 다음과 같이 정의한다.</p>
<p>$$
\alpha \cdot \beta = { r \cdot s \mid r \in \alpha,, s \in \beta,, r, s &gt; 0 }
$$</p>
<p>부호가 다른 실수의 곱셈은 자연스러운 부호 규칙을 적용하여 확장할 수 있다. 이 곱셈도 체의 공리를 만족한다.</p>
<hr>
<h3 id="📌-step-7-덧셈-역원의-정의-음수">📌 Step 7: 덧셈 역원의 정의 (음수)</h3>
<p>각 실수 $\alpha \in \mathbb{R}$에 대해 음수를 다음과 같이 정의한다.</p>
<p>$$
-\alpha = { r \in \mathbb{Q} \mid \exists s &gt; 0,\ -r - s \notin \alpha }
$$</p>
<p>이렇게 정의된 덧셈의 역원은 다음을 만족한다.</p>
<p>$$
\alpha + (-\alpha) = 0^*
$$</p>
<hr>
<h3 id="📌-step-8-곱셈-역원의-정의">📌 Step 8: 곱셈 역원의 정의</h3>
<p>양의 실수 $\alpha \in \mathbb{R}$의 곱셈의 역원을 다음과 같이 정의한다.</p>
<p>$$
\alpha^{-1} = { r \in \mathbb{Q} \mid r &gt; 0,\ \exists s &gt; r,\ s^{-1} \notin \alpha }
$$</p>
<p>이때 곱셈 역원은 다음과 같은 성질을 만족한다.</p>
<p>$$
\alpha \cdot \alpha^{-1} = 1^*
$$</p>
<hr>
<h3 id="📌-step-9-체field의-성질-확인">📌 Step 9: 체(Field)의 성질 확인</h3>
<p>위의 과정을 통해 구성한 덧셈과 곱셈 연산이 다음의 체의 공리를 만족함을 확인할 수 있다.</p>
<ul>
<li>덧셈과 곱셈의 교환법칙과 결합법칙 성립</li>
<li>분배법칙 성립</li>
<li>덧셈 항등원(0)과 곱셈 항등원(1) 존재</li>
<li>모든 원소는 덧셈 역원을 가짐</li>
<li>0을 제외한 모든 원소는 곱셈 역원을 가짐</li>
</ul>
<p>따라서, $\mathbb{R}$은 순서체이면서 최소 상계 성질을 갖는 완비체(complete ordered field)가 된다.</p>
<hr>
<h3 id="💡-결론">💡 결론</h3>
<p>이렇게 Dedekind 절단을 통해 유리수에서 실수를 엄밀히 구성할 수 있게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[📖 『Rudin: Principles of Mathematical Analysis』 Ch.1 (Introduction~1.11)
]]></title>
            <link>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.1</link>
            <guid>https://velog.io/@g1fted_13/%E3%80%8ERudin-Principles-of-Mathematical-Analysis%E3%80%8F-Ch.1</guid>
            <pubDate>Mon, 05 May 2025 04:36:09 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-introduction-실수-체계의-필요성">✅ Introduction (실수 체계의 필요성)</h2>
<ul>
<li><strong>유리수 체계($\mathbb{Q}$)는 불완전(incomplete)</strong> 하다.</li>
<li>예를 들어, 수열 $a_n = 1, 1.4, 1.41, 1.414, \dots$ 은 $\sqrt{2}$ 로 수렴하는 것처럼 보인다.</li>
<li>하지만 이 극한값인 $\sqrt{2}$ 는 <strong>유리수 집합 $\mathbb{Q}$ 안에는 존재하지 않는다</strong>.<ul>
<li>그렇다면 $\sqrt{2}$ 는 대체 무엇일까?</li>
<li>그리고 &quot;수렴한다(converge)&quot;는 개념을 어떻게 정확히 정의할 수 있을까?</li>
</ul>
</li>
</ul>
<p>이러한 질문이 바로 실수(real number) 개념을 필요로 하는 근본적인 이유이다.</p>
<hr>
<h2 id="✅-11-example">✅ 1.1 Example</h2>
<p>우선, 다음 명제를 생각하자:
<strong>명제</strong>: 방정식 $p^2 = 2$ 을 만족하는 유리수 $p$ 는 없다.</p>
<p>이 명제는 <strong>귀류법(proof by contradiction)</strong> 으로 증명할 수 있다. (증명은 생략)</p>
<hr>
<h3 id="추가적인-관찰-further-observations">추가적인 관찰 (Further Observations)</h3>
<p>1.  두 개의 유리수 집합 $A, B$ 를 다음과 같이 정의한다:</p>
<p>$$
A = {p \in \mathbb{Q} : p^2 &lt; 2 }, \quad B = {p \in \mathbb{Q} : p^2 &gt; 2 }
$$
이때, A와 B의 교집합은 없으며 합집합은 유리수 전체다.</p>
<p>2. 임의의 $p \in A$ 를 선택한 후, 아래와 같은 새로운 수 $q$ 를 정의하자:</p>
<p>$$
q = p - \frac{p^2 - 2}{p + 2} = \frac{2p + 2}{p + 2}
$$</p>
<p>그러면 이때, 다음 식이 성립한다:</p>
<p>$$
q^2 - 2 = \frac{2(p^2 - 2)}{(p + 2)^2}
$$</p>
<p>3. 여기서 분모는 항상 양수이므로, 분자의 부호가 전체 부호를 결정한다.<br>우리가 고른 $p \in A$ 는 정의상 $p^2 &lt; 2$ 이므로, $p^2 - 2 &lt; 0$ 이다. 따라서:</p>
<ul>
<li>$q^2 - 2 &lt; 0 \quad\Rightarrow\quad q^2 &lt; 2 \quad\Rightarrow\quad q \in A$</li>
</ul>
<p>또한:</p>
<ul>
<li>$\frac{p^2 - 2}{p + 2} &lt; 0$ 이므로, 원래 정의된 식 $q = p - \frac{p^2 - 2}{p + 2}$ 에서 빼는 값이 음수이다.  </li>
<li>따라서 $q &gt; p$ 이다.</li>
</ul>
<p>4. 즉, <strong>임의의 $p \in A$</strong>에 대해서 항상 <strong>더 큰 유리수 $q \in A$</strong> 를 찾을 수 있다.<br>이로써 집합 $A$ 는 절대로 <strong>최댓값(maximum)을 가지지 않음</strong>을 보였다.</p>
<p>5. 같은 방법으로, 집합 $B$ 도 최솟값(minimum)을 가지지 않음을 쉽게 확인할 수 있다. </p>
<hr>
<h2 id="✅-증명의-결론과-의미">✅ 증명의 결론과 의미:</h2>
<ul>
<li>우리는 유리수 전체를 두 집합 $A$ 와 $B$ 로 명확히 나누었다.</li>
<li>하지만 흥미롭게도 $A$ 의 최댓값과 $B$ 의 최솟값이 모두 존재하지 않는다.</li>
<li>분명히 두 집합 사이에는 어떤 수가 존재해야 할 것 같은데, 그 수는 유리수가 될 수 없다.</li>
<li>즉, 두 집합 사이의 &quot;gap(빈틈)&quot;이 존재하며, 이것이 바로 유리수 체계가 <strong>불완전하다</strong>는 증거이다.</li>
</ul>
<h3 id="🔑-요약">🔑 <strong>요약</strong>:</h3>
<blockquote>
<p><strong>유리수 집합($\mathbb{Q}$)은 밀도성(density)을 갖지만(모든 유리수쌍 사이에는 항상 또 다른 유리수 존재), 완비성(completeness)을 갖지 않는다.<br>그래서 우리는 이 빈틈을 채워주는 더 확장된 체계인 &quot;실수 체계(real number system)&quot;가 필요하다.</strong></p>
</blockquote>
<hr>
<h2 id="✅-13-definitions">✅ 1.3 Definitions</h2>
<ul>
<li>집합 $A$가 집합 $B$의 <strong>부분집합(subset)</strong> 이라는 것은,<br>집합 $A$의 모든 원소가 집합 $B$에도 포함됨을 의미하며, 다음과 같이 나타낸다:</li>
</ul>
<p>$$
A \subset B \quad \Longleftrightarrow \quad \text{($A$ is a subset of $B$)}
$$</p>
<ul>
<li><p>만약 $A \subset B$이면서 $A \ne B$인 경우, 즉,<br>$A$의 모든 원소가 $B$에 포함되지만 $B$에 속한 원소 중 적어도 하나는 $A$에 속하지 않는 경우를<br>$A$는 $B$의 <strong>진부분집합(proper subset)</strong> 이라 부른다.</p>
</li>
<li><p><strong>중요</strong>: </p>
</li>
</ul>
<blockquote>
<p><strong>만약 $A \subset B$이고 동시에 $B \subset A$라면</strong></p>
<p>$$
A = B
$$</p>
<p>라고 쓴다.<br>그렇지 않은 경우는 $A \ne B$라고 표기한다.</p>
</blockquote>
<hr>
<h2 id="✅-15-order-순서">✅ 1.5 Order (순서)</h2>
<p>집합 $S$ 위의 <strong>순서(order)</strong> 는 다음의 두 가지 성질을 만족하는 <strong>관계(relation)</strong> 이며, 일반적으로 &quot;$&lt;$&quot;로 나타낸다.</p>
<p>(i) 집합 $S$의 임의의 원소 $x, y$에 대해서 다음의 세 가지 명제 중 정확히 하나만 성립한다.</p>
<p>$$
x &lt; y,\quad x = y,\quad y &lt; x
$$</p>
<p>즉, 임의의 두 원소를 비교하면 항상 명확히 크거나, 작거나, 같거나 중 하나만 성립해야 한다.</p>
<p>(ii) 집합 $S$의 임의의 원소 $x, y, z$에 대해, 만약 $x &lt; y$이고 $y &lt; z$라면, 다음이 성립한다.</p>
<p>$$
x &lt; z
$$</p>
<p>즉, 순서 관계는 <strong>추이적(transitive)</strong> 이다.</p>
<h3 id="💡-cf-관계-relation">💡 cf. 관계 (relation)</h3>
<p>수학에서 말하는 <strong>관계(relation)</strong> 란 두 집합 $A$, $B$에 대해<br>순서쌍 $(a, b)$들의 모임, 즉 $R \subset A \times B$인 어떤 부분집합을 의미한다.<br>이것은 &quot;$a$와 $b$가 어떤 방식으로 연결되어 있다&quot;는 것을 수학적으로 나타내는 방법이다.</p>
<hr>
<h2 id="✅-16-ordered-set-순서집합">✅ 1.6 Ordered Set (순서집합)</h2>
<p>위에서 정의한 순서(order)가 부여된 집합을 <strong>순서집합(ordered set)</strong>이라 부른다.</p>
<p>즉, 집합 $S$가 <strong>순서집합</strong>이라는 것은, 집합 $S$ 위에 앞선 두 조건(i, ii)을 만족하는<br>명확한 순서 관계(&quot;$&lt;$&quot;)가 정의되어 있다는 것을 의미한다.</p>
<h2 id="✅-17--upper-bound--lower-bound-상계하계-★">✅ 1.7  Upper Bound &amp; Lower Bound (상계/하계) (★)</h2>
<p>집합 $S$가 <strong>순서집합(ordered set)</strong> 이고, $E \subset S$라고 하자.</p>
<p>만약 어떤 원소 $\beta \in S$가 존재하여,<br>모든 $x \in E$에 대해 $x \leq \beta$를 만족한다면,<br>우리는 집합 $E$가 <strong>위로 유계(bounded above)</strong> 라고 하고,<br>그러한 $\beta$를 $E$의 <strong>상계(upper bound)</strong> 라고 부른다.</p>
<p><strong>하계(lower bound)</strong> 는 이와 비슷하게, 
어떤 $\alpha \in S$가 모든 $x \in E$에 대해 $\alpha \leq x$를 만족하면 $\alpha$를 $E$의 <strong>하계</strong>라고 부른다.</p>
<hr>
<h2 id="✅-18-supremum--infimum-상한하한-★">✅ 1.8 Supremum &amp; Infimum (상한/하한) (★)</h2>
<p>집합 $S$가 순서집합(ordered set)이고 $E \subset S$이며 $E$가 <strong>위로 유계(bounded above)</strong> 라고 하자.<br>이때 다음 조건을 만족하는 어떤 $\alpha \in S$가 존재한다면, 
우리는 $\alpha$를 집합 $E$의 <strong>최소 상계(least upper bound)</strong> 또는 <strong>상한(supremum)</strong> 이라 하고, 다음과 같이 쓴다:</p>
<p>$$
\alpha = \sup E
$$</p>
<h3 id="조건">조건:</h3>
<ol>
<li><p>$\alpha$는 $E$의 <strong>upper bound(상계)</strong> 이다.<br>(즉, 모든 $x \in E$에 대해 $x \leq \alpha$)</p>
</li>
<li><p>임의의 $\gamma &lt; \alpha$는 $E$의 <strong>upper bound(상계)가 아니다</strong> .<br>(즉, $\gamma &lt; \alpha$이면 어떤 $x \in E$가 존재하여 $x &gt; \gamma$이다.)</p>
</li>
</ol>
<p>→ 이러한 $\alpha$는 <strong>최소 상계</strong> 이므로 유일하게 존재함이 &#39;조건 2&#39; 로부터 자명하다.</p>
<ul>
<li><strong>하한(infimum)</strong> 또는 <strong>최대 하계(greatest lower bound)</strong>는 동일한 방식으로 정의된다.</li>
</ul>
<hr>
<h2 id="✅-예시-from-11">✅ 예시 (from 1.1)</h2>
<ul>
<li><p>앞서 등장했던 집합 $A = { p \in \mathbb{Q} \mid p^2 &lt; 2 }$의 경우,<br>$A$의 모든 상계는 $B = { p \in \mathbb{Q} \mid p^2 &gt; 2 }$의 원소들과 정확히 같다.<br>즉, $B$가 $A$의 upper bounds를 구성한다.</p>
</li>
<li><p>하지만 $B$는 $\mathbb{Q}$에서 <strong>최솟값을 가지지 않으므로</strong>,  
$A$는 <strong>$\mathbb{Q}$ 내에서는</strong> least upper bound (즉, $\sup A$)를 가지지 않는다.</p>
</li>
</ul>
<hr>
<h3 id="💡-보충-설명">💡 보충 설명</h3>
<ul>
<li>일반적으로 $\alpha = \sup E$가 존재할 때, $\alpha$는 $E$의 원소일 수도 있고 아닐 수도 있다.<ul>
<li>$\alpha \in E$인 경우 → $\sup E$는 <strong>최댓값(maximum)</strong>이다.</li>
<li>$\alpha \notin E$인 경우 → $E$는 상한을 가지지만, <strong>최댓값은 없다.</strong><h2 id="✅--110-least-upper-bound-property-최소-상계-성질-★">✅  1.10 Least-upper-bound property (최소 상계 성질) (★)</h2>
</li>
</ul>
</li>
</ul>
<p>순서집합 $S$가 <strong>최소 상계 성질(least-upper-bound property)</strong> 을 만족한다는 것은 다음을 의미한다:</p>
<ul>
<li>임의의 공집합이 아닌 부분집합 $E \subset S$가 위로 유계(bounded above)일 때,<br>반드시 $E$의 <strong>상한(supremum)</strong>, 즉 $\sup E$가 집합 $S$ 안에 존재한다.</li>
</ul>
<blockquote>
<p><strong>예시</strong><br>앞서 살펴본 바와 같이, 유리수 집합 $\mathbb{Q}$는 최소 상계 성질을 만족하지 않는다.<br>(예: 집합 $A = { p \in \mathbb{Q} \mid p^2 &lt; 2 }$는 $\mathbb{Q}$ 내에서 위로 유계(bounded above)이지만 $\sup A$를 갖지 않음.)</p>
</blockquote>
<hr>
<h2 id="✅--111-least-upper-bound-property와-greatest-lower-bound-theory의-관계-★★">✅  1.11 Least-upper-bound property와 Greatest-lower-bound theory의 관계 (★★)</h2>
<p><strong>Least-upper-bound theroy</strong> 을 만족하는 모든 순서집합(ordered set)은 <strong>Greatest-lower-bound property(최대 하계 성질)</strong> 도 만족한다.</p>
<p>조금 더 엄밀히 서술하면:</p>
<ul>
<li>순서집합(Ordered Set) $S$가 Least-upper-bound property를 가진다고 하자.  </li>
<li>공집합이 아닌 부분집합 $B \subset S$가 아래로 유계(bounded below)라고 하자.  </li>
<li>집합 $B$의 모든 하계(lower bound)를 모은 집합을 $L$이라 하면,<br>$$
\alpha = \sup L
$$
가 반드시 집합 $S$ 안에 존재하며, 이때 다음이 성립한다:
$$
\alpha = \inf B
$$</li>
</ul>
<p>즉, 집합 $B$의 <strong>하한(infimum)</strong> 이 존재함을 의미한다.</p>
<hr>
<h3 id="📌-proof-증명">📌 <strong>Proof (증명)</strong></h3>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/0f9abbb6-2657-4e3d-be02-5835aec1123d/image.jpeg" alt=""></p>
<ul>
<li>$B$가 아래로 유계(bounded below)이므로, 하계들의 집합인 $L$은 비어있지 않다(적어도 하나의 원소를 가짐).</li>
<li>$L$은 정확히 집합 $B$의 모든 원소 $x \in B$에 대해 $y \leq x$ 를 만족하는 원소 $y \in S$로 이루어져 있다.<br>즉, $B$의 모든 원소 $x$는 $L$의 상계(upper bound)가 된다. 그러므로 $L$은 위로 유계(bounded above)이다.</li>
<li>최소 상계 성질(Least-upper-bound property) 가정에 따라 $L$은 $S$ 안에서 supremum을 가진다. 이를 $\alpha$라고 하자.</li>
</ul>
<p>이제 다음 두 가지를 보이면 된다.</p>
<ol>
<li><p><strong>$\alpha$가 실제로 집합 $B$의 하계임을 보이자.</strong></p>
<p>만약 $\gamma &lt; \alpha$ 라면, 정의에 따라 $\gamma$는 $L$의 upper bound가 될 수 없다. 
즉, $\gamma \notin B$임을 의미하며( $B$ 의 모든 원소는 L의 upper bound이기 때문), 
따라서 $B$의 모든 원소 $x$는 $\gamma$보다 크거나 같아야 하므로, 자연스럽게 모든 $x \in B$에 대해 $\alpha \leq x$가 성립한다. 즉, $\alpha$는 $B$의 하계이며, $\alpha \in L$이다.</p>
</li>
<li><p><strong>$\alpha$가 $B$의 최대 하계임을 보이자.</strong></p>
<p>만약 어떤 $\beta &gt; \alpha$가 있다면, $\alpha$가 $L$의 상계(supremum)이기 때문에 $\beta$는 더 이상 $L$에 포함될 수 없다. 따라서 $\beta$는 $B$의 하계가 아니다.  </p>
<p>즉, $\alpha$는 $B$의 하계 중 가장 큰 최대 하계가 된다. 이로써 $\alpha = \inf B$임이 증명된다.</p>
</li>
</ol>
<p>이로써 Least-upper-bound property를 만족하는 집합이 Greatest-lower-bound property도 만족함을 보였다 $\square$</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 31408 - 당직 근무표(C++) / Team Fortune Drill Week2]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-31408-%EB%8B%B9%EC%A7%81-%EA%B7%BC%EB%AC%B4%ED%91%9CC-Team-Fortune-Drill-Week2</link>
            <guid>https://velog.io/@g1fted_13/BOJ-31408-%EB%8B%B9%EC%A7%81-%EA%B7%BC%EB%AC%B4%ED%91%9CC-Team-Fortune-Drill-Week2</guid>
            <pubDate>Wed, 16 Apr 2025 08:59:17 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/31408">https://www.acmicpc.net/problem/31408</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/3546d854-597b-4320-b619-c4dcb84843eb/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-04-16">문제를 푼 날짜: 2025. 04. 16</h3>
<h3 id="implementation-math">#implementation #math</h3>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;queue&gt;
using namespace std;


const int  MAX = 100000;

int troop[MAX + 1]; // 0으로 초기화
priority_queue&lt;int&gt; pq;

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    int tmp;

    cin &gt;&gt; N;

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; tmp;
        troop[tmp]++;
    }

    for(int i = 1; i &lt;= MAX; i++){
        if(troop[i] != 0) pq.push(troop[i]);
    }  


    if(pq.top() &gt; (N+1) / 2) cout &lt;&lt; &quot;NO&quot;;
    else cout &lt;&lt; &quot;YES&quot;;

    return 0;
}</code></pre>
<hr>
<h3 id="✅-아이디어">✅ 아이디어</h3>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/1b89ac89-bfaf-4866-9839-cc1bc62d88d6/image.jpg" alt=""></p>
<p> 처음에는 입력을 받았을 때 문제의 입출력예시 설명처럼 위치를 바꾸는 식으로 해볼까 생각을 했었고, 그 다음에는 우선순위 큐를 이용해서 일정 조건을 만족하는지 여부를 큐가 empty할 때까지 반복하는 방법을 생각했었다. 그러다 보니 코드에 불필요하게 우선순위 큐가 사용되어있다. 그냥 반복문 돌리면서 max만 찾아도 상관없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 31864 - 눈송이 탕후루 만들기(C++) / Team Fortune Survival Week1]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-31864-%EB%88%88%EC%86%A1%EC%9D%B4-%ED%83%95%ED%9B%84%EB%A3%A8-%EB%A7%8C%EB%93%A4%EA%B8%B0C-Team-Fortune-Survival-Week1</link>
            <guid>https://velog.io/@g1fted_13/BOJ-31864-%EB%88%88%EC%86%A1%EC%9D%B4-%ED%83%95%ED%9B%84%EB%A3%A8-%EB%A7%8C%EB%93%A4%EA%B8%B0C-Team-Fortune-Survival-Week1</guid>
            <pubDate>Tue, 15 Apr 2025 07:23:31 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/31864">https://www.acmicpc.net/problem/31864</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/ee573898-746f-4298-9c15-292298519d7f/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-04-14">문제를 푼 날짜: 2025. 04. 14</h3>
<h3 id="binary_search-data_structures-geometry-hash_set-math-set">#binary_search #data_structures #geometry #hash_set #math #set</h3>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-cpp">/*
Naive Idea: 매 후보지마다 각 과일이 꼬치에 포함되는지 체크
-&gt; O(NM) : 9 * 10^10
-&gt; TLE! :(

Improved Idea: 
후보지의 핵심: 기울기 &amp; x부호 (단, y축 위의 점은 기울기 존재x이므로 따로 체크)
-&gt; 과일의 정보를 사전에 저장하면 되지 않을까?
-&gt; Worst Case: Y=X 직선 위에(X&gt;0) 모든 후보지와 과일이 다 존재하는 경우 -&gt; TLE :(

Any Better Idea??

과일의 정보 -&gt; (기울기, x좌표) 로 저장 : ex) key: 기울기[1] -&gt; value: -1, 3, 4 ... 
후보지의 정보 -&gt; (기울기, x좌표) 로 저장
             -&gt; 만약에 기울기가 같고 x좌표의 부호 역시 같다면 더 큰 것만 남기면 됨!

** Implementation?!
Initial Idea: map 활용, 기울기를 key로 사용

Question: 기울기가 같지만 x좌표의 부호가 다른 경우?
-&gt; 엄... 기울기를 key로 쓰고, value를 vector로 잡은 다음에 x좌표 양수 음수 각각 하나씩 저장하는 꼴?

+) 기울기는 float로 저장해야 할 듯?!
++) 기울기 존재하지 않는 경우 예외 처리하는 게 비효율적 -&gt; 기울기 상한이 10억이니 초과하는 기울기로 임의설정
-&gt; WA 뜬 후 발견! : x=0인 case는 부호 판정하는 부분에서 문제가 생겼닷!! x&lt;-&gt;y 스왑으로 해결하자..

+++) 기울기를 key로 사용하는 근본적 문제점(부동소수점문제)? 
-&gt; gcd로 나눈 (x, y) 사용
ex. (2, 4) -&gt; (1, 2) / (-3, -6) -&gt; (1, 2)

++++) 기울기 방향 고려하여 부호화
*/

#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;vector&gt;
#include &lt;cstdlib&gt;
#include &lt;numeric&gt;
#include &lt;utility&gt;

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N, M; // N: 과일의 수, M: 후보지의 수
    cin &gt;&gt; N &gt;&gt; M;

    int x, y; // 입력 위한 임시변수
    map&lt;pair&lt;int, int&gt;, vector&lt;int&gt;&gt; Fruits;
    map&lt;pair&lt;int, int&gt;, int&gt; Candidates;

    // 과일 위치 입력받기
    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; x &gt;&gt; y;

        // y축 위에 있으면 기울기 존재 x, y &gt; 0 이면 (0, 1), y &lt;0 이면 (0, -1) 생성
        if(x == 0){
            if(y &gt; 0) Fruits[{0, 1}].push_back(y);
            else Fruits[{0, -1}].push_back(y);
        } 
        else{
            int GCD = gcd(abs(x), abs(y));
            Fruits[{x/ GCD, y / GCD}].push_back(x);
        }
    }

    // 후보지 입력받기
    for(int i = 0; i &lt; M; i++){
        cin &gt;&gt; x &gt;&gt; y;

        pair&lt;int, int&gt; slope;
        // y축 위에 있으면 기울기 존재 x, y &gt; 0 이면 (0, 1), y &lt; 0이면 (0, -1)
        if(x == 0){
            if(y &gt; 0){
                slope = {0, 1};
                x = y;
            } else{
                slope = {0, -1};
                x = y;
            }
        }
        else{
            int GCD = gcd(abs(x), abs(y));
            slope = {x / GCD, y / GCD};
        }

        if(Candidates.find(slope) == Candidates.end()) Candidates[slope] = x;

        else if(abs(x) &gt; abs(Candidates[slope])) Candidates[slope] = x;
    }

    // 후보지 key(=기울기) 순회하면서 최댓값 찾기
    int max = 0;

    for(auto&amp; [key, value] : Candidates){
        int cnt = 0;
        for(auto&amp; fruit : Fruits[key]){
            if(abs(value) &gt;= abs(fruit)) cnt++;
        }
        if(cnt &gt; max) max = cnt;
    }

    cout &lt;&lt; max;
    return 0;
}</code></pre>
<hr>
<h3 id="✅-아이디어">✅ 아이디어</h3>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/e84488db-a37b-4eba-bb81-d308ecc48875/image.png" alt=""></p>
<ul>
<li>각 후보지마다 전체 과일을 iterate하면서 이 과일이 꼬치에 들어가는 지 체크? -&gt; TLE</li>
<li>후보지 &amp; 과일의 정보를 기울기로 저장하자! </li>
<li><blockquote>
<p>각 후보지마다 기울기가 같은 과일들만 조사하면 됨.
(단, y축 위 점의 경우 기울기 존재하지 않으므로 예외 처리 해야 함)</p>
</blockquote>
</li>
</ul>
<hr>
<h3 id="🧠-풀면서-틀렸던-부분--얻어가야-하는-내용">🧠 풀면서 틀렸던 부분 &amp; 얻어가야 하는 내용</h3>
<ul>
<li><p><strong>기울기를 key로 사용</strong>할 때, <strong>실수표현은 부동소수점 오차</strong>로 인해 의도한 바와 다르게 작동할 수 있음을 간과함!</p>
</li>
<li><blockquote>
<p><code>pair&lt;int, int&gt;</code>로 기울기 표현함. (기울기벡터 개념, <code>최대공약수로 (x, y)좌표 나눔</code>)</p>
</blockquote>
</li>
<li><p>기울기가 같더라도 방향이 다른 경우 (ex. (2, 4), (-3, -6))의 경우 다르게 취급해야 함!</p>
</li>
<li><blockquote>
<p>기울기 벡터 표현 시 부호 고려</p>
</blockquote>
</li>
<li><p>후보지의 경우 기울기 같고 방향마저 동일하다면 제일 멀리있는 후보지 하나만 고려하면 됨.</p>
</li>
<li><p>부호가 같은지 판별하는 조건식 사용할 때 곱하기를 사용했었는데
(<code>v * fruit &gt; 0</code>)
이 경우 <strong>int범위를 초과</strong>하는 문제가 발생하게 됨!
(<strong><code>자료형의 크기 초과하는 경우 없는지 항상 체크</code></strong>)</p>
</li>
<li><ul>
<li><code>pair&lt;int, int&gt;</code> =&gt; <code>&lt;utility&gt;</code></li>
<li><code>abs( int )</code>=&gt; <code>&lt;cstdlib&gt;</code></li>
<li><code>gcd(int x, int y)</code> =&gt; <code>&lt;numeric&gt;</code></li>
</ul>
</li>
<li><p>기울기를 사용하는 아이디어는 문제에 처음 접근할 때 (Survival Week1 당일) 바로 떠올렸음에도, 이후 구현 및 WA를 고치기 위해 꽤 많은 시간을 투자했음. 문제가 생겼을 때 빠르게 고칠 수 있는 클린 코드가 아닌, 그 순간순간 내 머릿속에 있는 아이디어를 배설하는 식의 주먹구구 코딩을 한 것이 큰 문제인 것 같다... 클린 코드를 쓰는 연습이 많이 필요해 보인다. 아래는 지피티가 개선해준 나의 코드이다.</p>
</li>
</ul>
<hr>
<h4 id="개선된-코드">개선된 코드</h4>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;vector&gt;
#include &lt;numeric&gt;  // std::gcd (C++17 이상)
#include &lt;cstdlib&gt;  // abs 함수
#include &lt;algorithm&gt;

using namespace std;
using Slope = pair&lt;int, int&gt;;

// 함수: 주어진 점 (x, y)에 대해 정규화된 기울기와 기준 좌표를 구한다.
// - 수직선(x==0)인 경우: y&gt;0이면 key = {0, 1}과 기준 = y, y&lt;0이면 key = {0, -1}과 기준 = y
// - 수직선이 아닌 경우: x, y를 gcd로 나누어 key = {x/g, y/g}와 기준 = x를 사용한다.
void normalizePoint(int x, int y, Slope &amp;slope, int &amp;pos) {
    if (x == 0) { 
        // 수직선인 경우 y&gt;0와 y&lt;0를 분리해서 처리
        if (y &gt; 0) {
            slope = {0, 1};
            pos = y;
        } else {
            slope = {0, -1};
            pos = y;
        }
    } else {
        int g = gcd(abs(x), abs(y));
        slope = {x / g, y / g};
        pos = x;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int nFruits, nCandidates;  
    cin &gt;&gt; nFruits &gt;&gt; nCandidates;

    // 과일들은 (정규화된 기울기, 기준 좌표) 형태로 저장
    // - 수직선인 경우, 기준은 y좌표, 그 외에는 x좌표
    map&lt;Slope, vector&lt;int&gt;&gt; fruits;
    // 후보지는 같은 기울기 그룹 내에서 양수와 음수 방향 각각 최고 값(원점에서 가장 멀리 떨어진 후보)만 저장
    // 따로 후보지 벡터를 관리하는 대신, 한 방향에 대해 단 하나의 후보지만 고려하면 됨
    map&lt;Slope, int&gt; candidate; 

    // 과일 입력
    for (int i = 0; i &lt; nFruits; i++) {
        int x, y;
        cin &gt;&gt; x &gt;&gt; y;
        Slope slp;
        int pos;
        normalizePoint(x, y, slp, pos);
        fruits[slp].push_back(pos);
    }

    // 후보지 입력: 같은 기울기 그룹 내에서는 기준 값의 절대값이 더 큰 후보지만 남긴다.
    for (int i = 0; i &lt; nCandidates; i++) {
        int x, y;
        cin &gt;&gt; x &gt;&gt; y;
        Slope slp;
        int pos;
        normalizePoint(x, y, slp, pos);

        // 처음 보는 그룹이면 바로 저장, 이미 있으면 절대값이 더 크면 갱신
        if (candidate.find(slp) == candidate.end() || abs(pos) &gt; abs(candidate[slp])) {
            candidate[slp] = pos;
        }
    }

    // 각 기울기 그룹 별로 후보지가 포함할 수 있는 과일의 개수를 셈.
    // 조건: 후보지(원점에서의 기준 좌표의 절대값)가 과일(해당 그룹에서 저장된 기준 좌표의 절대값)보다 크거나 같아야 한다.
    int ans = 0;
    for (const auto &amp; [slp, candPos] : candidate) {
        // 해당 기울기를 가진 과일이 없다면 넘어감
        if (fruits.find(slp) == fruits.end())
            continue;

        int count = 0;
        for (int fruitPos : fruits[slp]) {
            if (abs(candPos) &gt;= abs(fruitPos))
                count++;
        }
        ans = max(ans, count);
    }

    cout &lt;&lt; ans &lt;&lt; &quot;\n&quot;;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Team Fortune Survival Week1 FeedBack]]></title>
            <link>https://velog.io/@g1fted_13/Team-Fortune-Survival-Week1-FeedBack</link>
            <guid>https://velog.io/@g1fted_13/Team-Fortune-Survival-Week1-FeedBack</guid>
            <pubDate>Sat, 12 Apr 2025 15:00:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/g1fted_13/post/a03fd689-23bc-4ca7-94b6-120eae999917/image.jpeg" alt=""></p>
<h3 id="개괄">개괄</h3>
<p>코테 대비를 위해 대회 형식의 연습도 필요할 듯하여 3시간 제한으로 진행하였다. 제4회 SMUPC의 문제를 사용하였고, 싸지방에 모여서 진행했으나 중간에 뇌우조치로 작업가야해서(…) 흐름이 끊기긴 했다.</p>
<h3 id="a번-smupc-name-boj-31859-브론즈-1">A번. SMUPC NAME (BOJ 31859) (브론즈 1)</h3>
<h4 id="implementation-string">#implementation #string</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/9d0bdd28-f9ea-4b73-9647-d8f70f9e888f/image.jpeg" alt=""></p>
<h4 id="내-풀이">내 풀이</h4>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;
using namespace std;

int alphabet[26]; // &#39;a&#39;-&gt;0에 대응, 사용됐을 경우 1
int isAvailable[21]; // 제거할 시 1

int main() {

    string name; // 최대 20자
    string nickname = &quot;&quot;;
    int N;
    int cnt = 0; // 제거 문자 수

    cin &gt;&gt; N;
    cin &gt;&gt; name;

    for(int i = 0; i &lt; name.length(); i++){
        if(alphabet[name[i] - &#39;a&#39;] == 0) alphabet[name[i] - &#39;a&#39;] = 1;
        else{
            cnt++;
            isAvailable[i] = 1;
        }
    }

    for(int i = 0; i &lt; name.length(); i++){
        if(isAvailable[i] == 0) nickname += name[i];
    }

    cnt += 4;
    nickname += to_string(cnt);

    N += 1906;
    nickname = to_string(N) + nickname;

    reverse(nickname.begin(), nickname.end());

    nickname = &quot;smupc_&quot; + nickname;

    cout &lt;&lt; nickname;
    return 0;
}</code></pre>
<h4 id="개선된-풀이">개선된 풀이</h4>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    string name;
    cin &gt;&gt; name;

    // 알파벳 사용 여부를 저장하는 벡터 (false: 아직 안 씀, true: 이미 사용됨)
    vector&lt;bool&gt; used(26, false);

    // 중복되지 않는 문자를 담을 문자열
    string filteredName;
    // 중복된 문자(두 번째 이상 등장한 문자) 개수
    int duplicateCount = 0;

    for (char c : name) {
        int idx = c - &#39;a&#39;; 
        // 아직 사용되지 않은 알파벳이라면
        if (!used[idx]) {
            used[idx] = true;  
            filteredName.push_back(c);
        } else {
            // 이미 사용된 알파벳이면 중복 문자 수 증가
            duplicateCount++;
        }
    }

    // 문제 요구사항에 따라 중복 문자 수에 4를 더함
    duplicateCount += 4;

    // N에 1906을 더함
    N += 1906;

    // 닉네임: (N값) + (중복 제거된 문자열) + (중복 문자 수)
    string nickname = to_string(N) + filteredName + to_string(duplicateCount);

    // 문자열 뒤집기
    reverse(nickname.begin(), nickname.end());

    // &quot;smupc_&quot;를 접두어로 추가
    nickname = &quot;smupc_&quot; + nickname;

    // 결과 출력
    cout &lt;&lt; nickname &lt;&lt; &quot;\n&quot;;
    return 0;
}</code></pre>
<hr>
<h4 id="✅-풀면서-느낀-점">✅ 풀면서 느낀 점</h4>
<ul>
<li>빠르게 풀고 넘겼어야 할 A번 문제이지만, string 오랜만에 다루면서 구현에 어려움을 겪고 16분 걸림</li>
<li><blockquote>
<p>String 문제 풀 것!</p>
</blockquote>
</li>
</ul>
<hr>
<h3 id="b번-열심히-일하는-중-boj-31860-실버-2">B번. 열심히 일하는 중 (BOJ 31860) (실버 2)</h3>
<h4 id="implementation-data_structures-simulation-priority_queue">#implementation #data_structures #simulation #priority_queue</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/3bfa7d7c-c891-430f-ab83-6461c15b7cdf/image.jpeg" alt=""></p>
<h4 id="내-풀이-1">내 풀이</h4>
<pre><code class="language-cpp">/*
일의 수: N
일 처리 후 M만큼 중요도 감소
중요도 K 이하로 떨어지면 그 일은 완료.

만족도 = (전날 만족도 / 2) + 오늘 한 일 중요도
(첫날의 경우 전날 만족도 0으로 가정)

Goal1: 일 끝내는데 얼마나 걸림?
Goal2: 매일매일 만족도

Idea: priority_queue를 활용해보자!
*/

#include &lt;iostream&gt;
#include &lt;queue&gt;
#include &lt;vector&gt;
using namespace std;

priority_queue&lt;int&gt; work; // 중요도 크기 내림차순으로 정렬
vector&lt;int&gt; daily_satisfaction;

int main() {
    int N, M, K;
    int tmp;
    int day = 0; // 업무 끝나는 데 걸리는 일 수
    int satisfaction = 0; // 전날 만족도

    cin &gt;&gt; N &gt;&gt; M &gt;&gt; K;

    for(int i = 0; i &lt; N; i++){
        cin &gt;&gt; tmp;
        work.push(tmp);
    }

    while(!work.empty()){
        int importance = work.top();
        day++;
        work.pop();

        satisfaction = satisfaction / 2 + importance;
        daily_satisfaction.push_back(satisfaction);

        importance -= M;
        if(importance &gt; K) work.push(importance);
    }

    cout &lt;&lt; day &lt;&lt; &#39;\n&#39;;
    for(auto &amp;k : daily_satisfaction){
        cout &lt;&lt; k &lt;&lt; &#39;\n&#39;;
    }
    return 0;
}</code></pre>
<hr>
<h4 id="✅-풀면서-느낀-점-1">✅ 풀면서 느낀 점</h4>
<ul>
<li>16분 소요 -&gt; 무난하게 넘김!</li>
<li>문제 접근: ‘매 순간 중요도가 제일 높은 작업 택해야함‘ </li>
<li><blockquote>
<p>‘중요도 순으로 항상 정렬‘</p>
</blockquote>
</li>
<li><blockquote>
<p><code>priority queue</code>!</p>
</blockquote>
</li>
<li>코테 때 나올 문제도 텍스트양이 많아 독해를 빠르게 잘 하는 것이  관건일 것 -&gt; 문제의 조건 써보면서 문제 이해하기!</li>
</ul>
<hr>
<h3 id="c번-비밀번호-만들기-boj-31861-실버-1">C번. 비밀번호 만들기 (BOJ 31861) (실버 1)</h3>
<h4 id="dp">#dp</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/8ea09769-c24e-487c-aa51-434941be1c38/image.jpeg" alt=""></p>
<h4 id="내-풀이-2">내 풀이</h4>
<pre><code class="language-cpp">/*
IDEA: DP 문제?!

DP[n][k]
n번째 문자(1&lt;=n&lt;=26) 뒤에 문자열 길이 k (0&lt;=k&lt;1000) 경우의수

기저사례
DP[n][0] = 1

DP[n][1] = {현재 문자: x, 가능한 범위: 1&lt;=a&lt;=26 , |a-x| &gt;= N}일 때 a의 개수


Given N, M
=&gt; we need to find DP[1][M-1] + DP[2][M-1] + ... + DP[26][M-1]!
*/

#include &lt;iostream&gt;
using namespace std;

int DP[27][1000];
int N, M;
int findDP(int a, int b);

int main() {

    int sum = 0; // 구하고자 하는 경우의 수
    cin &gt;&gt; N &gt;&gt; M;

    // DP배열 초기화
    for(int i = 1; i &lt;= 26; i++){
        for(int j = 0; j &lt;= M - 1; j++){
            DP[i][j] = -1;
        }
    }

    for(int i = 1; i &lt;= 26; i++){
        sum = (sum + findDP(i, M-1)) % 1000000007;
    }

    cout &lt;&lt; sum;
    return 0;
}

int findDP(int a, int b){
    if(DP[a][b] != -1) return DP[a][b];
    else if(b == 0){
        DP[a][b] = 1;
        return 1;
    }
    else if(b == 1){
        int cnt = 0;
        for(int i = 1; i &lt;= 26; i++){
            if(abs(i - a) &gt;= N) cnt++;
        }
        DP[a][b] = cnt;
        return cnt;
    }
    else{
        int sum = 0;
        for(int i = 1; i &lt;= 26; i++){
            if(abs(i - a) &gt;= N){
                sum = (sum + findDP(i, b - 1)) % 1000000007;
            }
        }
        DP[a][b] = sum;
        return sum;
    }
}</code></pre>
<hr>
<h4 id="✅-풀면서-느낀-점-2">✅ 풀면서 느낀 점</h4>
<ul>
<li>28분 소요 -&gt; 잘 풀었음!</li>
<li>문제 접근: ‘가능한 경우의 수 카운팅‘ + ‘int형 범위 초과하는 정답‘</li>
<li><blockquote>
<p><code>DP를 사용하지 않을까?</code></p>
</blockquote>
</li>
<li><blockquote>
<p> 무엇을 기록(재사용)해야 할까?</p>
</blockquote>
</li>
<li>DP 문제를 풀 때 구해야 할 2가지<ul>
<li><code>점화식</code></li>
<li><code>기저사례(base case)</code>
더 구하기 쉬운 기저사례를 먼저 떠올리고 점화식에 접근하니 잘 풀 수 있었음!</li>
</ul>
</li>
</ul>
<hr>
<h3 id="d번--내진-설계-boj-31863-골드-5">D번.  내진 설계 (BOJ 31863) (골드 5)</h3>
<h4 id="graphs-graph_traversal-implementation-simulation">#graphs #graph_traversal #implementation #simulation</h4>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/08fa0f75-5717-4586-b580-156d43e4a64a/image.jpeg" alt=""></p>
<h4 id="내-풀이-대회-시간-후-완성">내 풀이 (대회 시간 후 완성)</h4>
<pre><code class="language-cpp">/*
IDEA: 시키는 그대로 하자!

지진의 종류가 2개. → 본진(2칸) &amp; 여진(1칸)
핵심 → 본진은 어차피 처음 1번만! 이후에는 여진만 발생 가능.
→ 본진을 따로 처리해주고, 여진을 queue로 처리해주자!

*/
#include &lt;iostream&gt;
#include &lt;queue&gt;
using namespace std;

char Map[1001][1001];

struct Position{
    int x, y;
};

int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};

queue&lt;Position&gt; q;
int collapse = 0; // 무너진 건물 수

// 건물 만났을 때 처리 방식 똑같으므로 함수로 처리해주자!
void WeakBuilding(int x, int y);
void StrongBuilding(int x, int y);

int main() {
    int N, M;
    int num = 0; // 기존 건물 수

    Position source;

    cin &gt;&gt; N &gt;&gt; M;

    // 맵 입력받기 &amp; 진원지 찾아주기
    for(int i = 0; i &lt; N; i++){
        for(int j = 0; j &lt; M; j++){
            cin &gt;&gt; Map[i][j];
            if(Map[i][j] == &#39;@&#39;){
                source.x = i;
                source.y = j;
            }

            else if(Map[i][j] == &#39;*&#39; || Map[i][j] == &#39;#&#39;) num++;
        }
    }

    // 본진 처리해주기
    for(int i = 0; i &lt; 4; i++){
        int pointx = source.x + dx[i];
        int pointy = source.y + dy[i];

        if(pointx &gt;= 0 &amp;&amp; pointx &lt; N &amp;&amp; pointy &gt;= 0 &amp;&amp; pointy &lt; M){
            // 방파제일 경우 -&gt; 여기서 끝!
            if(Map[pointx][pointy] == &#39;|&#39;) continue;

            // 내진X 건물 -&gt; 건물 무너짐 &amp; 여진 발생!
            else if(Map[pointx][pointy] == &#39;*&#39;) WeakBuilding(pointx, pointy);

            // 내진O 건물 -&gt; 내진X 건물로 바뀜!
            else if(Map[pointx][pointy] == &#39;#&#39;) StrongBuilding(pointx, pointy);

            // 방파제 아닌 case이므로 =&gt; 두 칸 갈 수 있는지 체크!
            pointx += dx[i];
            pointy += dy[i];
            if(pointx &gt;= 0 &amp;&amp; pointx &lt; N &amp;&amp; pointy &gt;= 0 &amp;&amp; pointy &lt; M){
                if(Map[pointx][pointy] == &#39;*&#39;) WeakBuilding(pointx, pointy);
                else if(Map[pointx][pointy] == &#39;#&#39;) StrongBuilding(pointx, pointy);
            }
        }
    }

    // 여진 처리해주기
    while(!q.empty()){
        int pointx = q.front().x;
        int pointy = q.front().y;

        q.pop();

        for(int i = 0; i &lt; 4; i++){
            int newpointx = pointx + dx[i];
            int newpointy = pointy + dy[i];

            if(newpointx &gt;= 0 &amp;&amp; newpointx &lt; N &amp;&amp; newpointy &gt;= 0 &amp;&amp; newpointy &lt; M){
                if(Map[newpointx][newpointy] == &#39;|&#39;) continue;
                else if(Map[newpointx][newpointy] == &#39;*&#39;) WeakBuilding(newpointx, newpointy);
                else if(Map[newpointx][newpointy] == &#39;#&#39;) StrongBuilding(newpointx, newpointy);
            }

        }
    }

    cout &lt;&lt; collapse &lt;&lt; &#39; &#39; &lt;&lt; num - collapse;
    return 0;
}

void WeakBuilding(int x, int y){
    collapse++;
    Map[x][y] = &#39;.&#39;;

    Position tmp;
    tmp.x = x;
    tmp.y = y;
    q.push(tmp);
}

void StrongBuilding(int x, int y){
    Map[x][y] = &#39;*&#39;;
}</code></pre>
<hr>
<h4 id="✅-풀면서-느낀-점-3">✅ 풀면서 느낀 점</h4>
<ul>
<li>이 문제에서 시간을 꽤 소요했고, 결정적으로 풀었음에도 WA(Wrong Answer)가 나와 멘붕이 오면서 나머지 문제를 못 풀고 이 문제마저 해결하지 못하게 되었다.</li>
<li>로직이 어려운 문제가 전혀 아니었고, 내가 생각한 바와 같이 ‘시키는 대로‘ 하면 되는 문제였다.</li>
<li>어이없게도 내 로직 역시 맞았고, 
<code>int num = 0;</code> 을 <code>int num;</code>으로 쓴 것 하나만이 문제였다.<ul>
<li>온라인 컴파일러를 사용하였는데, 주어진 예제도 맞고, 내가 만든 사이드케이스 역시 문제없이 실행됐던 이유는 아마 컴파일러 버전이 높아 초기화하지 않은 변수를 0으로 알아서 초기화했기 때문(?)으로 생각한다.<h4 id="🧠-피드백">🧠 피드백</h4>
이 문제에서 발생한 패착은 크게 2가지</li>
</ul>
</li>
<li><ul>
<li><ol>
<li>로직이 어렵지 않았음에도 최초풀이를 완성하는 데 시간이 걸린 점** </li>
</ol>
</li>
</ul>
</li>
<li><ul>
<li><ol start="2">
<li>테케가 맞았음에도 WA가 나오는 상황에 대처하지 못한 점**
이다.</li>
</ol>
</li>
</ul>
</li>
</ul>
<p><strong>1번</strong>의 문제는 결국 집중력 문제로, 제한된 시간 안에 빠르게 문제를 정확히 푸는 훈련을 많이 하는 것이 주요할 것으로 보인다. 
(수능에 비유하자면, 80분 동안 국어 시험지 한 세트를 푸는 연습)</p>
<p><strong>2번</strong>의 문제는 <code>Online Judge와 내가 사용한 컴파일러의 환경이 다를 수 있다</code>는 점을 몰랐던 것, 그리고 내 코드를 <code>꼼꼼히 디버깅하지 못한 점</code>에서 기인한다. </p>
<p>** ‘ 어떤 전략을 세울 것인가? ‘<strong>와도 결부된다. 분명히 내 로직이 맞다는 확신이 있음에도 미상의 원인으로 WA가 뜰 때, 뒷 문제들 또한 로직을 바로 떠올릴 수 있다면 일단 뒷 문제부터 푸는 것이 좋은 전략이 되겠지만, 뒤의 문제들이 쉽게 풀기 힘든 난이도라면 이 문제는 ** 절대 틀려서는 안 될 문제일</strong> 것이고, 코드를 처음부터 파헤쳐 봤어야 했다. 앞으로 같은 경우가 발생한다면 <code>변수 선언</code>에서 문제가 없는 지도 꼭 체크해야겠다.</p>
<hr>
<p>나머지 문제들은 시간 내에 읽지 못했거나, 조금 접근하다 말았음으로 개별 문제를 따로 푼 후 피드백할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BOJ 11725 - 트리의 부모 찾기(C++) / Team Fortune Drill Week1]]></title>
            <link>https://velog.io/@g1fted_13/BOJ-11725-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%B6%80%EB%AA%A8-%EC%B0%BE%EA%B8%B0C-Team-Fortune-Drill-Week1</link>
            <guid>https://velog.io/@g1fted_13/BOJ-11725-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%B6%80%EB%AA%A8-%EC%B0%BE%EA%B8%B0C-Team-Fortune-Drill-Week1</guid>
            <pubDate>Fri, 11 Apr 2025 15:01:31 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/11725">https://www.acmicpc.net/problem/11725</a>
<img src="https://velog.velcdn.com/images/g1fted_13/post/ec01c7ad-cf3f-4b4c-be35-b84cd57968e1/image.png" alt=""></p>
<h3 id="문제를-푼-날짜-2025-04-11">문제를 푼 날짜: 2025. 04. 11</h3>
<h3 id="graphs-graph_traversal-trees-bfs-dfs">#graphs #graph_traversal #trees #bfs #dfs</h3>
<p><img src="https://velog.velcdn.com/images/g1fted_13/post/f7d87301-4d9e-47e3-b373-6792d46fb918/image.png" alt="">
<strong>[Figure 1]</strong> <strong>문제를 이해하는 과정에서 싸지방 윈도우의 그림판을 이용해서 그려봤다. 상당한 퀄리티이다.</strong></p>
<h3 id="문제를-푼-날짜-2025-04-11-1">문제를 푼 날짜: 2025. 04. 11</h3>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
using namespace std;

const int MAX = 100000;
vector&lt;int&gt; adj_list[MAX + 1];
int Parent[MAX + 1]; // 아직 부모노드 못 찾은 상태(초기값) == 0
queue&lt;int&gt; q;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N;
    cin &gt;&gt; N;

    // 인접 리스트 업데이트
    for(int i = 0; i &lt; N; i++){
        int tmp1,tmp2;
        cin &gt;&gt; tmp1 &gt;&gt; tmp2;
        adj_list[tmp1].push_back(tmp2);
        adj_list[tmp2].push_back(tmp1);
    }

    int x = 1;
    for(auto &amp;k : adj_list[1]){
        Parent[k] = 1;
        q.push(k);
    }

    while(!q.empty()){
        x = q.front();
        q.pop();

        for(auto &amp;k: adj_list[x]){
            if(Parent[k] == 0){
                Parent[k] = x;
                q.push(k);
            }
        }
    }

    for(int i = 2; i &lt;= N; i++){
        cout &lt;&lt; Parent[i] &lt;&lt; &#39;\n&#39;;
    }
    return 0;
}</code></pre>
<p>** 이번 문제는 피드백할 점이 딱히 없었다 ㅎㅎ ** </p>
]]></description>
        </item>
    </channel>
</rss>