<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dduk.ddak.log</title>
        <link>https://velog.io/</link>
        <description>생각이 현실이 될 수 있도록 노력하는 중입니다.</description>
        <lastBuildDate>Tue, 20 Jan 2026 03:39:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dduk.ddak.log</title>
            <url>https://velog.velcdn.com/images/melon-chicken/profile/45162ad7-f034-4cb6-8159-539f87dedb90/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dduk.ddak.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/melon-chicken" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[백준 문제 풀이] 15649번 N과 M (1)]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-15649%EB%B2%88-N%EA%B3%BC-M-1</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-15649%EB%B2%88-N%EA%B3%BC-M-1</guid>
            <pubDate>Tue, 20 Jan 2026 03:39:47 GMT</pubDate>
            <description><![CDATA[<h1 id="15649-n과-m-1">[15649] N과 M (1)</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-20</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/14ebe765-4331-4654-bafe-85dc6e2ad3e0/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 백트래킹(Backtracking), 순열</li>
<li><strong>요구사항</strong>: 1부터 N까지의 자연수 중에서 <strong>중복 없이</strong> M개를 고른 <strong>모든 순열</strong>을 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>int[]</code> : 현재 선택된 수열을 저장</li>
<li><code>boolean[]</code> : 숫자 사용 여부(중복 방지) 체크</li>
<li><code>StringBuilder</code> : 출력 성능 최적화</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>재귀 (recursion)</li>
<li>백트래킹 (backtracking)</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>순열 (permutation)</li>
<li>깊이 (depth)</li>
<li>상태 복원 (backtracking)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<blockquote>
<h3 id="1-문제-분해">1. 문제 분해</h3>
</blockquote>
<ul>
<li>길이가 M인 수열을 만든다.</li>
<li>각 자리에 1부터 N까지의 숫자를 하나씩 시도한다.</li>
<li>이미 사용한 숫자는 다시 사용할 수 없다.</li>
<li>길이가 M이 되면 하나의 완성된 순열로 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<hr>
<blockquote>
<h3 id="2-재귀-함수의-목적-정의-핵심">2. 재귀 함수의 목적 정의 (핵심)</h3>
<p><strong><code>sequence(n, depth)</code>의 역할</strong></p>
</blockquote>
<ul>
<li><code>depth == M</code>
→ 길이 M의 수열이 완성되었으므로 출력하고 종료한다.</li>
<li>그렇지 않으면
→ 1부터 N까지 순회하면서 아직 사용하지 않은 숫자를 선택한다.<blockquote>
</blockquote>
</li>
</ul>
<hr>
<blockquote>
</blockquote>
<h3 id="3-핵심-로직-흐름">3. 핵심 로직 흐름</h3>
<blockquote>
</blockquote>
<pre><code class="language-text">sequence(depth):
    if depth == M:
        수열 출력
        return
&gt;
    for i = 1 to N:
        if i가 아직 사용되지 않았다면:
            i를 선택
            sequence(depth + 1)
            i 선택 취소 (상태 복원)</code></pre>
<blockquote>
</blockquote>
<hr>
<blockquote>
</blockquote>
<h3 id="4-백트래킹의-본질">4. 백트래킹의 본질</h3>
<blockquote>
</blockquote>
<ul>
<li><strong>선택</strong>: <code>visited[i] = true</code>, <code>tempArr[depth] = i</code></li>
<li><strong>재귀 호출</strong>: 다음 자리로 이동</li>
<li><strong>복원</strong>: <code>visited[i] = false</code><blockquote>
</blockquote>
</li>
</ul>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;

class Main {
    public static int[] tempArr;
    public static boolean[] visited;
    public static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int r = sc.nextInt();

        tempArr = new int[r];
        visited = new boolean[n + 1];

        sequence(n, 0);
        System.out.println(sb);
    }

    public static void sequence(int n, int depth) {
        if (depth == tempArr.length) {
            for (int i = 0; i &lt; tempArr.length; i++) {
                sb.append(tempArr[i]).append(&quot; &quot;);
            }
            sb.append(&quot;\n&quot;);
            return;
        }

        for (int i = 1; i &lt;= n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                tempArr[depth] = i;
                sequence(n, depth + 1);
                visited[i] = false; // 상태 복원
            }
        }
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><strong>시간 복잡도</strong>: O(N! / (N−M)!)</li>
<li><strong>공간 복잡도</strong>: O(N + M)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>재귀를 사용해야 한다는 것은 알았지만 <strong>재귀 함수 하나가 정확히 무엇을 책임지는지</strong>를 정의하지 못해 접근이 어려웠다.</li>
<li>“이 함수가 한 번 호출될 때 정확히 무엇을 결정하는가”를 잡지 못해 로직을 외부 설명에 의존하게 되었다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>재귀 문제에서는 반드시 <strong>“이 함수는 어떤 단위의 문제를 해결하는가”</strong>를 먼저 문장으로 정의해야 한다.</li>
<li>백트래킹은 <code>선택 → 재귀 → 복원</code> 이 3단계가 항상 세트로 움직인다.</li>
<li>순열/조합 문제에서 <code>depth</code>는 <strong>현재까지 몇 개를 골랐는지</strong>를 의미하는 지표로 이해하면 훨씬 명확해진다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/15649">https://www.acmicpc.net/problem/15649</a></li>
<li>참고 자료: <a href="https://velog.io/@gayeong39/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-BackTracking">https://velog.io/@gayeong39/백트래킹-알고리즘-BackTracking</a></li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p><strong>비슷한 유형 (GPT 추천)</strong></p>
<ul>
<li><a href="https://www.acmicpc.net/problem/15650">백준 - 15650번 N과 M (2)</a></li>
<li><a href="https://www.acmicpc.net/problem/15651">백준 - 15651번 N과 M (3)</a></li>
</ul>
</li>
<li><p><strong>확장 문제 (GPT 추천)</strong></p>
<ul>
<li><a href="https://www.acmicpc.net/problem/9742">백준 - 9742번 순열</a></li>
<li><a href="https://www.acmicpc.net/problem/6603">백준 - 6603번 중복 없는 조합</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 2447번 별 찍기 - 10]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2447%EB%B2%88-%EB%B3%84-%EC%B0%8D%EA%B8%B0-10</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2447%EB%B2%88-%EB%B3%84-%EC%B0%8D%EA%B8%B0-10</guid>
            <pubDate>Mon, 19 Jan 2026 03:14:02 GMT</pubDate>
            <description><![CDATA[<h1 id="2447-별-찍기---10">[2447] 별 찍기 - 10</h1>
<blockquote>
<p>난이도: ★★★☆☆  •  solved on: 2026-01-19</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/5068dfb6-24e8-4e97-8cb5-523ad75585c3/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 재귀, 분할 정복</li>
<li><strong>요구사항</strong>: 크기 N의 정사각형에 대해 <strong>중앙이 비어 있는 별 패턴</strong>을 재귀적으로 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>List&lt;String&gt;</code> : 각 줄을 문자열 단위로 관리</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>재귀 (Recursion)</li>
<li>분할 정복 (Divide and Conquer)</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>패턴 생성 (pattern generation)</li>
<li>재귀적 구조 (recursive structure)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>N이 1일 때는 <code>&quot;*&quot;</code> 하나로 종료되는 <strong>기저 조건</strong>을 둔다.</li>
<li>N이 1보다 클 경우, 크기 <code>n/3</code>의 별 패턴을 먼저 만든다.</li>
<li>만들어진 작은 패턴을 이용해<blockquote>
</blockquote>
<ul>
<li>위: <code>***</code></li>
<li>중간: <code>* *</code></li>
<li>아래: <code>***</code>
형태로 결합한다.<blockquote>
</blockquote>
</li>
</ul>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">getStars(n):
    if n == 1:
        return [&quot;*&quot;]
&gt;
    small = getStars(n / 3)
&gt;
    for s in small:
        add s + s + s
&gt;
    for s in small:
        add s + blank + s
&gt;
    for s in small:
        add s + s + s</code></pre>
<blockquote>
</blockquote>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li><code>n == 1</code> 인 경우 바로 리스트를 반환하여 재귀를 종료한다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        List&lt;String&gt; result = getStars(n);
        StringBuilder sb = new StringBuilder();
        for(String s : result) {
            sb.append(s).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }

    public static List&lt;String&gt; getStars(int n) {

        List&lt;String&gt; result = new ArrayList&lt;&gt;();

        if(n == 1) {
            result.add(&quot;*&quot;);
            return result;
        }

        List&lt;String&gt; smallStars = getStars(n / 3);
        for(String s : smallStars) {
            result.add(s + s + s);
        }

        String blank = &quot;&quot;;
        for(int i = 0; i &lt; n / 3; i++){
            blank += &quot; &quot;;
        }

        for(String s : smallStars) {
            result.add(s + blank + s);
        }

        for(String s : smallStars) {
            result.add(s + s + s);
        }
        return result;
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><p><strong>시간 복잡도</strong>: O(N²)</p>
<ul>
<li>최종적으로 N×N 크기의 별 패턴을 생성해야 하므로 출력량 자체가 O(N²)이다.</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: O(N²)</p>
<ul>
<li>모든 줄을 문자열로 저장하는 리스트 때문이며, 재귀 호출 스택은 O(log₃N) 수준이다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>처음에 <strong>접근 자체가 되지 않았다</strong>.</li>
<li>재귀를 쓰는 것은 알겠지만, 반환 타입을 <code>String</code>, <code>List&lt;String&gt;</code>, <code>String[][]</code> 중에서 무엇으로 할지 결정하지 못해 설계 단계에서 막혔다.</li>
<li>결국 재귀 함수의 역할을 <strong>n 크기의 패턴 전체를 반환한다</strong>로 명확히 정의하면서 해결할 수 있었다. (<code>int</code>로 받고 <code>List&lt;String&gt;</code>으로 리턴하기)</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><p>재귀 문제에서는 <strong>함수의 역할을 한 문장으로 정의하는 것</strong>이 매우 중요하다.</p>
<ul>
<li><p>재귀 함수는 유닛 단위로 반복하는 것이기 때문</p>
</li>
<li><p>예: <code>getStars(n)</code>은 <strong>n 크기의 별 패턴을 문자열 리스트로 반환한다</strong></p>
</li>
</ul>
</li>
<li><p>출력 형태가 줄 단위로 고정되어 있다면 <code>List&lt;String&gt;</code>이 매우 자연스럽다.</p>
</li>
<li><p>중앙을 비우는 문제는 대부분 <strong>3등분 구조</strong>를 가지므로, 먼저 작은 패턴을 만든 뒤 조합하는 방식이 핵심이다.</p>
</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/2447">https://www.acmicpc.net/problem/2447</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p><strong>비슷한 유형 (GPT 추천)</strong></p>
<ul>
<li><a href="https://www.acmicpc.net/problem/4779">백준 - 4779번 칸토어 집합</a></li>
<li><a href="https://www.acmicpc.net/problem/10993">백준 - 10993번 별 찍기 - 18</a></li>
</ul>
</li>
<li><p><strong>확장 문제 (GPT 추천)</strong></p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10991">백준 - 10991번 별 찍기 - 16</a></li>
<li><a href="https://www.acmicpc.net/problem/10997">백준 - 10997번 별 찍기 - 22</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 20920번 영단어 암기는 괴로워]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-20920%EB%B2%88-%EC%98%81%EB%8B%A8%EC%96%B4-%EC%95%94%EA%B8%B0%EB%8A%94-%EA%B4%B4%EB%A1%9C%EC%9B%8C</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-20920%EB%B2%88-%EC%98%81%EB%8B%A8%EC%96%B4-%EC%95%94%EA%B8%B0%EB%8A%94-%EA%B4%B4%EB%A1%9C%EC%9B%8C</guid>
            <pubDate>Sat, 17 Jan 2026 08:21:34 GMT</pubDate>
            <description><![CDATA[<h1 id="20920-영단어-암기는-괴로워">[20920] 영단어 암기는 괴로워</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-17</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/c11fba7d-e5dd-430a-9451-ae1f3301b41b/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><p><strong>문제 유형</strong>: 문자열 처리, 정렬, 해시</p>
</li>
<li><p><strong>요구사항</strong>:</p>
<ul>
<li><p>길이가 <strong>M 이상인 단어만</strong> 대상으로 한다</p>
</li>
<li><p>단어를 다음 기준으로 정렬해야 한다</p>
<ol>
<li>자주 나오는 단어일수록 앞에 온다</li>
<li>길이가 긴 단어일수록 앞에 온다</li>
<li>사전 순으로 앞서는 단어일수록 앞에 온다</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashMap&lt;String, EnglishWord&gt;</code> : 단어별 등장 횟수 저장</li>
<li><code>List&lt;EnglishWord&gt;</code> : 정렬을 위한 리스트</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>해시를 이용한 빈도 카운팅</li>
<li><code>Comparable</code> 구현을 통한 커스텀 정렬</li>
<li>스트림 정렬 (<code>stream().sorted()</code>)</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>빈도 기반 정렬 (frequency sort)</li>
<li>다중 조건 정렬 (multi-level sort)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--custom-comparator">방법 1 : Custom Comparator</h3>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>단어를 입력받으며 길이가 M 미만이면 무시한다</li>
<li><code>HashMap</code>에 단어를 key로, 등장 정보를 value로 저장한다</li>
<li>이미 등장한 단어면 횟수만 증가시킨다<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>정렬 전략</strong><blockquote>
</blockquote>
<ul>
<li><code>EnglishWord</code> 클래스에서 <code>Comparable</code>을 구현한다</li>
<li>정렬 기준<blockquote>
</blockquote>
<ol>
<li>등장 횟수 오름차순</li>
<li>단어 길이 오름차순</li>
<li>사전 역순<blockquote>
</blockquote>
</li>
</ol>
</li>
<li><code>Collections.reverseOrder()</code>로 전체 정렬 결과를 뒤집는다</li>
</ul>
</li>
<li><strong>출력</strong><blockquote>
</blockquote>
<ul>
<li>정렬된 리스트를 순회하며 단어만 출력한다</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] cmds = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(cmds[0]);
        int m = Integer.parseInt(cmds[1]);

        HashMap&lt;String, EnglishWord&gt; map = new HashMap&lt;&gt;();

        for(int i = 0; i &lt; n; i++) {
            String word = br.readLine();
            if(word.length() &lt; m) continue;

            if(!map.containsKey(word)) {
                map.put(word, new EnglishWord(word));
            } else {
                map.get(word).updateCnt();
            }
        }

        List&lt;EnglishWord&gt; list = map.values().stream()
                .sorted(Collections.reverseOrder())
                .toList();

        StringBuilder sb = new StringBuilder();
        for(EnglishWord word : list) {
            sb.append(word.word).append(&quot;\n&quot;);
        }

        System.out.println(sb);
    }

    static class EnglishWord implements Comparable&lt;EnglishWord&gt; {
        String word;
        int cnt;

        public EnglishWord(String word) {
            this.word = word;
            this.cnt = 1;
        }

        @Override
        public int compareTo(EnglishWord anotherWord){
            if(cnt - anotherWord.cnt != 0){
                return cnt - anotherWord.cnt;
            }
            if(word.length() - anotherWord.word.length() != 0){
                return word.length() - anotherWord.word.length();
            }
            return -1 * word.compareTo(anotherWord.word);
        }

        public void updateCnt(){
            cnt++;
        }
    }
}</code></pre>
<hr>
<h3 id="방법-2--custom-class-없이-comparator-활용">방법 2 : Custom Class 없이 Comparator 활용</h3>
<blockquote>
<ol>
<li><strong>개선 포인트</strong></li>
</ol>
</blockquote>
<ul>
<li>별도의 클래스를 만들지 않는다</li>
<li>정렬 기준을 <code>Comparator</code> 체이닝으로 한눈에 드러낸다</li>
<li><code>reverseOrder()</code>를 쓰지 않아도 된다<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>정렬 기준 명시</strong><blockquote>
</blockquote>
<ul>
<li>빈도 내림차순</li>
<li>길이 내림차순</li>
<li>사전 오름차순</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;
import java.util.stream.*;

class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();

        for(int i = 0; i &lt; n; i++) {
            String word = br.readLine();
            if(word.length() &lt; m) continue;
            map.put(word, map.getOrDefault(word, 0) + 1);
        }

        List&lt;String&gt; result = map.keySet().stream()
                .sorted((a, b) -&gt; {
                    if(map.get(a) != map.get(b))
                        return map.get(b) - map.get(a);
                    if(a.length() != b.length())
                        return b.length() - a.length();
                    return a.compareTo(b);
                })
                .toList();

        StringBuilder sb = new StringBuilder();
        for(String w : result) sb.append(w).append(&quot;\n&quot;);
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N log N)</li>
<li><strong>공간 복잡도</strong>: O(N)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N log N)</li>
<li><strong>공간 복잡도</strong>: O(N)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>HashMap의 value 객체들을 기준에 맞게 정렬하려다 보니 클래스를 만들고 Comparable을 구현하는 과정이 길어졌다</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><strong>정렬 기준이 명확한 문제는 Comparator가 가독성이 더 좋다</strong></li>
<li><code>Comparable + reverseOrder()</code> 방식은 기준이 많아질수록 직관성이 떨어질 수 있다</li>
<li>단순 빈도 문제는 <code>Map&lt;String, Integer&gt;</code> 구조만으로도 충분하다</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/20920">https://www.acmicpc.net/problem/20920</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1302">백준 - 1302반 베스트셀러</a></li>
<li><a href="https://www.acmicpc.net/problem/11536">백준 - 11536번 줄 세우기</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1181">백준 - 1181번 단어 정렬</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 2108번 통계학]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2108%EB%B2%88-%ED%86%B5%EA%B3%84%ED%95%99</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2108%EB%B2%88-%ED%86%B5%EA%B3%84%ED%95%99</guid>
            <pubDate>Sat, 17 Jan 2026 07:28:26 GMT</pubDate>
            <description><![CDATA[<h1 id="2108-통계학">[2108] 통계학</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-17</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/64dcf335-345d-4f48-89cb-4b8fed0e58be/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 정렬, 구현, 해시/카운팅</li>
<li><strong>요구사항</strong>: N개의 정수에 대해 <strong>산술평균(반올림)</strong>, <strong>중앙값</strong>, <strong>최빈값</strong>, <strong>범위</strong>를 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li>방법 1: <code>int[]</code>, <code>HashMap&lt;Integer,Integer&gt;</code>, <code>ArrayList&lt;Integer&gt;</code></li>
<li>방법 2: <code>int[] count (8001)</code> 카운팅 배열</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>방법 1: 정렬 + 해시맵 빈도</li>
<li>방법 2: 카운팅 (Counting) + 누적합 스캔</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>반올림 (rounding)</li>
<li>최빈값 tie-breaking (동률 시 두 번째로 작은 값)</li>
<li>값 범위 고정 (-4000~4000)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--hashmap--정렬">방법 1 : HashMap + 정렬</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>입력을 받으면서 <code>min</code>, <code>max</code>, <code>total</code>을 갱신한다.</li>
<li><code>HashMap</code>으로 각 수의 빈도를 센다.</li>
<li>최빈값 후보를 <code>ArrayList</code>에 모은 뒤 정렬로 규칙(두 번째로 작은 값)을 맞춘다.</li>
<li><code>arr</code>를 정렬해 중앙값을 구한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">입력:
    min/max/total 갱신
    map[num]++
&gt;
map 순회:
    최대 빈도 cnt 갱신
    cnt와 같으면 후보 추가
    cnt보다 크면 후보 초기화 후 추가
&gt;
arr 정렬 -&gt; 중앙값
후보 정렬 -&gt; 최빈값 결정(두 번째로 작은 값)</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>평균은 <code>Math.round(1.0 * total / n)</code> 형태로 반올림한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*; 
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n =  Integer.parseInt(br.readLine());

        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        int total = 0;
        int freq = 4001;
        int cnt = 0;
        ArrayList&lt;Integer&gt; arrayList = new ArrayList&lt;&gt;();
        HashMap&lt;Integer,Integer&gt; map = new HashMap&lt;&gt;();
        int[] arr = new int[n];
        map.put(freq, -1);

        for(int i = 0; i &lt; n; i++){
            int temp =  Integer.parseInt(br.readLine());
            if(temp &lt; min) min = temp;
            if(temp &gt; max) max = temp;

            total += temp;

            if(map.containsKey(temp)){
                map.put(temp,map.get(temp)+1);
            } else {
                map.put(temp,1);
            }
            arr[i] = temp;
        }
        for(Integer key : map.keySet()){
            if(map.get(key).equals(-1)){
                continue;
            }
            if(map.get(key).equals(cnt)){
                arrayList.add(key);
            }
            if(map.get(key).compareTo(cnt)&gt;0){
                arrayList.clear();
                arrayList.add(key);
                cnt = map.get(key);
            }
        }
        Arrays.sort(arr);
        arrayList.sort(Collections.reverseOrder());
        System.out.println(Math.round(1.0 * total / n));
        System.out.println(arr[n/2]);
        if(arrayList.size() == 1){
            System.out.println(arrayList.get(0));
        } else {
            System.out.println(arrayList.get(arrayList.size()-2));
        }
        System.out.println(max-min);
    }
}</code></pre>
<hr>
<h3 id="방법-2--카운팅-배열로-동시-처리">방법 2 : 카운팅 배열로 동시 처리</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>값 범위가 <code>-4000 ~ 4000</code>으로 고정이므로 <code>count[8001]</code>로 빈도를 센다(인덱스는 <code>x + 4000</code>).</li>
<li>평균: <code>sum / n</code>을 <code>Math.round</code>로 반올림한다.</li>
<li>중앙값: 누적 빈도가 <code>(n+1)/2</code> 이상이 되는 지점을 찾는다(문제에서 N은 홀수).</li>
<li>최빈값: 최대 빈도를 찾은 뒤, <strong>오름차순 스캔 중 최빈값을 만나는 두 번째 값</strong>을 답으로 한다(동률 규칙 충족).</li>
<li>범위: <code>max - min</code>을 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">입력:
    sum, min, max 갱신
    count[x+4000]++
&gt;
median:
    누적합 &gt;= (n+1)/2 되는 최초 값
&gt;
mode:
    maxFreq 구함
    i=0..8000 오름차순으로 스캔하며
        count[i]==maxFreq 인 값을 발견할 때마다 found++
        found==1이면 mode=값
        found==2이면 mode=값(두 번째로 작은 값)로 확정</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>평균 반올림은 음수도 포함하므로 <code>Math.round((double)sum / n)</code>으로 처리한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;

class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        int[] count = new int[8001]; // -4000~4000 =&gt; +4000 shift
        long sum = 0;

        int min = 4001;
        int max = -4001;

        for (int i = 0; i &lt; n; i++) {
            int x = Integer.parseInt(br.readLine());
            sum += x;
            if (x &lt; min) min = x;
            if (x &gt; max) max = x;
            count[x + 4000]++;
        }

        // 1) 산술평균
        int mean = (int) Math.round((double) sum / n);

        // 2) 중앙값 (N은 홀수)
        int target = (n + 1) / 2;
        int cumulative = 0;
        int median = 0;
        for (int i = 0; i &lt; 8001; i++) {
            cumulative += count[i];
            if (cumulative &gt;= target) {
                median = i - 4000;
                break;
            }
        }

        // 3) 최빈값 (여러 개면 두 번째로 작은 값)
        int maxFreq = 0;
        for (int i = 0; i &lt; 8001; i++) {
            if (count[i] &gt; maxFreq) maxFreq = count[i];
        }

        int mode = 0;
        int found = 0;
        for (int i = 0; i &lt; 8001; i++) {
            if (count[i] == maxFreq) {
                mode = i - 4000;
                found++;
                if (found == 2) break; // 두 번째로 작은 최빈값 확정
            }
        }

        // 4) 범위
        int range = max - min;

        StringBuilder sb = new StringBuilder();
        sb.append(mean).append(&#39;\n&#39;);
        sb.append(median).append(&#39;\n&#39;);
        sb.append(mode).append(&#39;\n&#39;);
        sb.append(range).append(&#39;\n&#39;);
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N log N) (정렬)</li>
<li><strong>공간 복잡도</strong>: O(N) (배열 + 맵/리스트)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + K) (K=8001 고정) → 사실상 O(N)</li>
<li><strong>공간 복잡도</strong>: O(K) → 사실상 O(1)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>최빈값을 어떻게 저장하고 찾아낼지에 대해 골머리를 앓았다 처음 접근 (방법1)에서는 따로 <code>HashMap</code>을 저장하여 <code>cnt</code>를 확인했는데 나중 접근을 통해 카운팅 배열을 활용해볼 수 있다는 걸 확인했다. </li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>값의 범위가 작고 고정이면 <code>HashMap</code>정렬보다 <strong>카운팅 배열</strong>이 구현도 단순하고 성능도 안정적이다.</li>
<li>최빈값 동률 규칙은 정렬 후 두 번째를 요구하므로, <strong>오름차순 스캔 중 두 번째로 만나는 최빈값</strong>을 고르는 방식이 가장 안전하다. (시간 복잡도는 증가하나, 공간복잡도는 간결하다)</li>
<li>평균 반올림은 음수도 포함하므로 <code>Math.round((double)sum / n)</code>처럼 double로 변환해 처리하는 편이 명확하다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/2108">https://www.acmicpc.net/problem/2108</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10989">백준 - 10989번 수 정렬하기 3</a></li>
<li><a href="https://www.acmicpc.net/problem/2587">백준 - 2587번 대표값2</a></li>
<li><a href="https://www.acmicpc.net/problem/2592">백준 - 2592번 최빈수 구하기</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/11650">백준 - 11650번 좌표 정렬하기</a></li>
<li><a href="https://www.hackerrank.com/challenges/30-operators/problem">Hackerrank - Day 0: Mean, Median, and Mode</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1037번 약수]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1037%EB%B2%88-%EC%95%BD%EC%88%98</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1037%EB%B2%88-%EC%95%BD%EC%88%98</guid>
            <pubDate>Thu, 15 Jan 2026 02:29:58 GMT</pubDate>
            <description><![CDATA[<h1 id="1037-약수">[1037] 약수</h1>
<blockquote>
<p>난이도: ★☆☆☆☆  •  solved on: 2026-01-15</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/fa1b67dd-fcee-4868-827e-781c7a4922b4/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 수학, 약수 성질</li>
<li><strong>요구사항</strong>: 어떤 수 (N)의 <strong>진짜 약수들</strong>(1과 (N) 제외)이 주어질 때, (N)을 구해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li>입력 순회(최솟값/최댓값 갱신)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>약수의 <strong>짝(pair) 성질</strong> 이용</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>약수 쌍(divisor pair), 최소/최대 약수</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<h3 id="왜-최소-약수-×-최대-약수--n-이-되는가">왜 최소 약수 × 최대 약수 = N 이 되는가?</h3>
<p>핵심은 <strong>약수는 항상 쌍으로 존재한다</strong>는 성질이다.</p>
<ul>
<li>어떤 수 $N$에 대해 약수 $d$가 있으면, 항상 $N/d$도 약수이다.</li>
<li>따라서 약수들은 $(d, N/d)$ 형태의 <strong>짝</strong>으로 묶인다.</li>
<li>이때 각 짝의 곱은 항상 ($d \times (N/d) = N$) 이다.</li>
</ul>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());

        long max = Long.MIN_VALUE;
        long min = Long.MAX_VALUE;

        for (int i = 0; i &lt; n; i++) {
            long temp = Long.parseLong(st.nextToken());
            if (temp &gt; max) max = temp;
            if (temp &lt; min) min = temp;
        }

        System.out.println(min * max);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><strong>시간 복잡도</strong>: O(n)</li>
<li><strong>공간 복잡도</strong>: O(1)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>왜 최소 약수와 최대 약수를 곱하면 원래 수 N이 되는지 직관적으로 이해가 안됐다. 하지만 테스트 케이스를 기준으로 규칙성을 찾아 이해는 안되지만 구현하니 됐다.</li>
<li>“약수는 쌍으로 묶이고, 가장 작은 약수의 짝이 가장 큰 약수”라는 연결이 바로 떠오르지 않았다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>약수 문제는 대부분 <strong>짝(pair) 성질</strong>을 떠올리면 식이 단순해진다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1037">https://www.acmicpc.net/problem/1037</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/2501">백준 - 2501번 약수 구하기</a></li>
<li><a href="https://www.acmicpc.net/problem/9506">백준 - 9506번 약수들의 합</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/2609">백준 - 2609번 최대공약수와 최소공배수</a></li>
<li><a href="https://www.acmicpc.net/problem/3036">백준 - 3036번 링</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 24511번 queuestack]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-24511%EB%B2%88-queuestack</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-24511%EB%B2%88-queuestack</guid>
            <pubDate>Tue, 13 Jan 2026 06:24:20 GMT</pubDate>
            <description><![CDATA[<h1 id="24511-queuestack">[24511] queuestack</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-13</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/38361a99-e5d8-4fc2-abcc-47bfa821265e/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 자료구조(Queue/Stack), 시뮬레이션 최적화</li>
<li><strong>요구사항</strong>: 주어진 <code>type</code>(0=queue, 1=stack)과 초기 값들로 구성된 구조에 대해, 입력 <code>m</code>개를 순서대로 넣고 최종적으로 빠져나오는 값을 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>Deque</code> : 앞/뒤 삽입·삭제가 O(1)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>불필요한 시뮬레이션 제거</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>queue만 결과에 영향</li>
<li>초기 queue 값의 “흐름”을 한 덩어리로 처리</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--시뮬레이션-기반-풀이-시간-초과로-실패">방법 1 : 시뮬레이션 기반 풀이 (시간 초과로 실패)</h3>
<blockquote>
<p>전체를 매 입력마다 끝까지 시뮬레이션해서 <code>m × n</code>이 발생하는 구조라 시간 복잡도가 커진다.</p>
</blockquote>
<h4 id="deque를-활용해-구현">Deque를 활용해 구현</h4>
<pre><code class="language-java">//  queuestack

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());

        ArrayList&lt;Deque&lt;Integer&gt;&gt; arr = new ArrayList&lt;&gt;();
        String[] types = br.readLine().split(&quot; &quot;);
        String[] values = br.readLine().split(&quot; &quot;);

        int m = Integer.parseInt(br.readLine());

        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);
        StringBuilder sb = new StringBuilder();
        String num = &quot;&quot;;
        String temp = &quot;&quot;;
        for(int i = 0; i &lt; m; i++){
            num = st.nextToken();
            for(int j = 0; j &lt; n; j++){
                if(types[j].equals(&quot;0&quot;)){
                    temp =  values[j];
                    values[j] = num;
                    num = temp;
                } else {
                    continue;
                }
            }

            sb.append(num+&quot; &quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<h4 id="배열을-활용해-구현">배열을 활용해 구현</h4>
<pre><code class="language-java">//  queuestack

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());

        ArrayList&lt;Deque&lt;Integer&gt;&gt; arr = new ArrayList&lt;&gt;();
        int[] types = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);
        StringTokenizer st2 = new StringTokenizer(br.readLine(), &quot; &quot;);

        int m = Integer.parseInt(br.readLine());

        StringTokenizer st3 = new StringTokenizer(br.readLine(), &quot; &quot;);
        StringBuilder sb = new StringBuilder();

        for(int i=0;i&lt;n;i++){
            arr.add(new ArrayDeque&lt;&gt;());
            types[i] = Integer.parseInt(st.nextToken());
            arr.get(i).add(Integer.parseInt(st2.nextToken()));
        }
        int num;
        for(int i = 0; i &lt; m; i++){
            arr.get(0).add(Integer.parseInt(st3.nextToken()));
            for(int j = 0; j &lt; n-1; j++){
                if(types[j] == 0){
                    num = arr.get(j).pollFirst();
                } else {
                    num = arr.get(j).pollLast();
                }
                arr.get(j+1).add(num);
            }

            if(types[n-1] == 0){
                num = arr.get(n-1).pollFirst();
            } else {
                num = arr.get(n-1).pollLast();
            }
            sb.append(num+&quot; &quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--관찰로-onm-만들기">방법 2 : 관찰로 O(n+m) 만들기</h3>
<blockquote>
<ol>
<li><strong>stack(type=1)은 “넣고 바로 빼는 효과”</strong>만 내서 최종 출력 흐름에 남는 값이 없다.</li>
<li><strong>queue(type=0)의 초기 값들만</strong> 결과 스트림에 영향을 준다.</li>
<li>따라서 <strong>초기 queue 값들을 한 번에 모아두고</strong>, 매 쿼리마다 <code>입력값 push → 맨 앞 pop</code>만 하면 된다.</li>
</ol>
</blockquote>
<p><strong>핵심 로직 흐름</strong></p>
<blockquote>
</blockquote>
<pre><code class="language-text">q = deque()
&gt;
// 초기 상태에서 type=0(queue)인 값만 모음
for i in 0..n-1:
    if type[i] == 0:
        q.addLast(value[i])
&gt;
// 실제 출력은 가장 &quot;앞에서&quot; 빠져나오므로,
// 위에서 모은 queue 값들의 순서를 &quot;역방향&quot;으로 맞춰야 한다.
// (구현에서는 q에 넣는 순서를 뒤에서부터 넣거나, 앞에 addFirst로 넣는다)
&gt;
for each x in queries:
    q.addLast(x)
    print q.pollFirst()</code></pre>
<p><strong>Java 코드</strong></p>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        int[] type = new int[n];
        int[] val = new int[n];

        StringTokenizer st1 = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; n; i++) type[i] = Integer.parseInt(st1.nextToken());

        StringTokenizer st2 = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; n; i++) val[i] = Integer.parseInt(st2.nextToken());

        int m = Integer.parseInt(br.readLine());
        StringTokenizer st3 = new StringTokenizer(br.readLine());
        StringBuilder sb = new StringBuilder();

        Deque&lt;Integer&gt; q = new ArrayDeque&lt;&gt;();

        for (int i = n - 1; i &gt;= 0; i--) {
            if (type[i] == 0) q.addFirst(val[i]);
        }

        for (int i = 0; i &lt; m; i++) {
            int x = Integer.parseInt(st3.nextToken());
            q.addLast(x);
            sb.append(q.pollFirst()).append(&#39; &#39;);
        }

        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(n × m)</li>
<li><strong>공간 복잡도</strong>: O(n)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(n + m)</li>
<li><strong>공간 복잡도</strong>: O(n)  (type=0인 값 개수만큼)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>처음 실제 시뮬레이션 기반의 풀이에서 시간복잡도를 어떻게 더 줄여야 하는지 감이 잡히지 않았다.</li>
<li>매 쿼리마다 n개 구조를 끝까지 시뮬레이션하는 방식에서 병목이 생겼다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>전체를 그대로 시뮬레이션하지 말고, <strong>출력에 실제로 영향을 주는 요소만 남기는 관찰</strong>이 핵심이다.</li>
<li>이 문제는 <code>type=1(stack)</code>이 결과 흐름에서 <strong>사라지는 요소</strong>라서, <strong>type=0(queue)만 모아 한 번에 처리</strong>하면 된다.</li>
<li><code>Deque</code>로 <code>addLast / pollFirst</code>만 하면 쿼리당 O(1)로 끝난다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/24511">https://www.acmicpc.net/problem/24511</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/12789">백준 - 12789번 도키도키 간식드리미</a></li>
<li><a href="https://www.acmicpc.net/problem/18258">백준 - 18258번 큐 2</a></li>
<li><a href="https://www.acmicpc.net/problem/10866">백준 - 10866번 덱</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1021">백준 - 1021번 회전하는 큐</a></li>
<li><a href="https://www.acmicpc.net/problem/1966">백준 - 1966번 프린터 큐</a></li>
<li><a href="https://www.hackerrank.com/challenges/queue-using-two-stacks/problem">HackerRank - Queue using Two Stacks</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Reading Note] The Birth of Deep CNNs: Why AlexNet Worked]]></title>
            <link>https://velog.io/@melon-chicken/Research-Note-The-Birth-of-Deep-CNNs-Why-AlexNet-Worked</link>
            <guid>https://velog.io/@melon-chicken/Research-Note-The-Birth-of-Deep-CNNs-Why-AlexNet-Worked</guid>
            <pubDate>Sun, 04 Jan 2026 15:19:01 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며</h1>
<blockquote>
<p>이 글은 딥러닝 역사에서 전환점으로 평가받는 AlexNet 논문을 읽으며,
이 모델이 왜 “작동했는가”를 정리하기 위해 작성되었습니다.</p>
</blockquote>
<p>성능 비교가 아니라, 당시 기술적 제약 속에서 어떤 선택들이 문제를 해결했는지를 중심으로 살펴보고자 합니다. 또한 이 글은 Standford University의 CS231n의 일부를 참고로 하였습니다. 따라서 Computer Vision에 대해 더 궁금하신 독자께서는 링크를 참고해주세요. <a href="https://cs231n.stanford.edu/schedule#:~:text=Date%20Description%20Lecturer%20Course%20Materials,Image%20Classification%20Problem%20Linear%20Classification"><strong>link</strong></a></p>
<h2 id="paper-information">Paper Information</h2>
<blockquote>
<p><strong>Title:</strong> ImageNet Classification with Deep Convolutional Neural Networks</p>
<p><strong>Authors:</strong> Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton</p>
<p><strong>Journal:</strong> Advances in Neural Information Processing Systems (<strong>NeurIPS 2012</strong>) <a href="https://papers.nips.cc/paper_files/paper/2012/hash/c399862d3b9d6b76c8436e924a68c45b-Abstract.html"><strong>link</strong></a></p>
</blockquote>
<p>arXiv ID: <strong>NFI</strong> </p>
<hr>
<h1 id="ⅰ-introduction">Ⅰ. Introduction</h1>
<blockquote>
<p><em>“To recognize the real world, how much capacity does a model really need?”</em></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/bd268fdf-3693-46c5-8c4b-93385c39bd0d/image.png" alt=""></p>
<figcaption style="text-align:center; font-size:15px; color:#808080;">Figure 2: An illustration of the architecture of our CNN (AlexNet), explicitly showing the delineation of responsibilities
between the two GPUs.</figcaption>

<h2 id="ⅰ1-데이터-규모와-문제-복잡성"><strong>Ⅰ.1. 데이터 규모와 문제 복잡성</strong></h2>
<p>이 논문에 대한 이야기를 하기 전, 먼저 이러한 모델이 나올 수 있었고 효과가 있었던 배경에 대해 설명하려 합니다.</p>
<p>디지털 사진이 막 도입된 시점 (1960s), 객체 탐지 (Object Recognition)은 상당히 어려운 분야였습니다. 대상이 어디있는가? 어디까지 대상으로 할 것인가?에 대한 Vision Representation에 대한 고전적인 질문과 함께 (Stages of Visual Representation, David Marr, 1970s) 아직은 디지털 데이터에 대해 정리하고 확립하는 과정이었습니다. </p>
<p>그 과정에서 나온 데이터 셋은 <a href="https://cs.nyu.edu/~yann/data/norb-v1.0/">NORB</a>와 <a href="https://www.kaggle.com/datasets/jessicali9530/caltech256">Caltech-101/256</a>,
<a href="https://www.cs.toronto.edu/~kriz/cifar.html">CIFAR-10/100</a> 등이 있습니다. 이들은 당시 기준으로는 표준적이고 잘 만들어진 데이터셋이지만 데이터의 규모는 객체 인식 연구를 하기에는 충분하지 않은 규모 (수만 장)였습니다.</p>
<p>물론 간단한 인식은 labrl-preserving transformation릏 통한 데이터 증강을 활용하여 이 규모에서도 꽤나 잘 이루어졌지만 문제는 가려짐, 배경 변화, 시점 변화 같은 상황의 경우 단순한 증강으로는 한계점이 있어 더 많은 데이터가 필요해졌습니다. </p>
<p>이때 등장한 것이 바로 <a href="https://www.image-net.org/about.php">ImageNet</a>이었습니다. ImageNet은 2009년 대규모 객체 인식을 위해 등장한 이미지 데이터셋으로. 한 객체 범주 (synset) 당 평균 1000개의 이미지라는 대규모의 데이터셋입니다. 이로써 객체 인식을 위한 데이터의 규모는 갖추어졌지만, 많은 데이터가 필요해짐에 따라 또한 모델의 용량도 이에 맞춰 더 커져야 했습니다.</p>
<hr>
<h2 id="ⅰ2-모델-용량capacity의-필요성"><strong>Ⅰ.2. 모델 용량(Capacity)의 필요성</strong></h2>
<p>데이터의 규모가 커진 만큼 데이터의 복잡도 (e.g. 시점 변화, 조명 변화, 배경 변화...)도 느러 연구진은 CNN을 선택하게 됩니다. CNN의 특성은 바로 다음 섹션에서 설명합니다.</p>
<p>이러한 배경에서 연구진들은 다음과 같은 선택을 통해 모델의 용량을 키워 AlexNet의 성능을 끌어올립니다.</p>
<blockquote>
<ul>
<li>약 6천만개의 파라미터</li>
</ul>
</blockquote>
<ul>
<li>5개의 Convolution Layer와 3개의 Fully Connected Layer</li>
<li>상대적으로 큰 Neural Network</li>
</ul>
<hr>
<h1 id="ⅱ-key-design-choices">Ⅱ. Key Design Choices</h1>
<blockquote>
<p><em>Architecture is not just structure; it is prior knowledge.</em></p>
</blockquote>
<h2 id="ⅱ1-convolution-neural-network-cnn">Ⅱ.1. Convolution Neural Network (CNN)</h2>
<p>CNN은 기존의 Neural Network에 기반하였기에 학습 가능한 weights와 bias를 가진 neuron을 가지고 있습니다. 하지만 가장 큰 변화점은 입력값이 이미지라는 가정하에 다양한 이미지의 특성을 구조에 녹일 수 있다는 점입니다. </p>
<p>다시 말해 <strong>&quot;이미지의 내부에는 인접한 픽셀 간의 강한 상관관계가 있다 (Locality)&quot;</strong>라는 가정과 <strong>&quot;이미지의 통계적 특성은 픽셀의 위치에 따라 크게 변하지 않는다 (Stationarity)&quot;</strong>라는 가정을 전제하에 두고 있습니다. </p>
<p>그렇기에 기존의 Nerual Network와는 다르게 더 적은 connection와 parameter를 가지고 더 쉽게 학습 가능하게 됩니다. 물론 이때 약간의 성능하락은 존재하지만 여전히 매력적인 선택지입니다.</p>
<p>하지만 그럼 왜 진작 사용하지 않았는가? 라고 묻는다면 이 모델을 실행할 하드웨어 자원이 부족했기 때문입니다. 하지만 연구진은 당시에 개발된 GPU (GTX 580 3GB GPU)를 CNN에 맞게 적용하여 사용해서 큰 CNN 모델에 대한 학습을 가능하게 합니다. 이를 연구진은 아래와 같이 말합니다.</p>
<blockquote>
<p>Luckily, current GPUs, <strong>paired with a highly-optimized implementation of 2D convolution</strong>, are powerful enough to facilitate the training of interestingly-large CNNs, and recent datasets such as ImageNet contain enough labeled examples to train such models without severe overfitting.</p>
</blockquote>
<p>하지만 연구진은 Convolution layer만 사용하지 않고 기존의 Fully connected network 역시 사용합니다. (It contains eight learned layers — five convolutional and three fully-connected.)</p>
<blockquote>
<p><strong>Fully-connected layer란?</strong>
한 레이어의 모든 뉴런이 바로 이전 레이어의 모든 뉴런과 연결되어 있는 레이어를 말합니다.</p>
<ol>
<li>convolutional layer</li>
</ol>
</blockquote>
<ul>
<li>국소 영역 (local receptive field)만 봄</li>
<li>가중치 공유 (weight sharing)</li>
<li>공간 구조 (spatial structure)를 유지</li>
</ul>
<hr>
<ol start="2">
<li>fully-connected layer</li>
</ol>
<ul>
<li>공간 구조를 더 이상 고려하지 않음</li>
<li>입력 전체를 하나의 벡터로 보고</li>
<li>모든 입력을 종합해서 판단</li>
</ul>
<hr>
<h2 id="ⅱ2-rectified-linear-units-relus-nonlinearity">Ⅱ.2. Rectified Linear Units (ReLUs) Nonlinearity</h2>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/142ffedd-4df5-479d-9fb2-624eeb77eb20/image.png" alt=""></p>
<figcaption style="text-align:center; font-size:15px; color:#808080;">Figure 1: A four-layer convolutional neural network with ReLUs (solid line) reaches a 25% training error rate on CIFAR-10 six times faster than an equivalent network with tanh neurons
(dashed line).</figcaption>

<p>기존의 $tanh$, sigmoid 계열의 활성화 함수는 깊은 네트워크 (입력값의 절댓값이 커질 수록) gradient saturation 문제를 유발하게 됩니다. (e.g. $sigmoid(x) = (1 + e^{-x})^{-1}$) 이런 문제를 해결하고자 연구진은 $f(x) = max(0,x)$ ReLU를 활성화 함수로 지정하여 이러한 saturation 문제를 해결합니다.</p>
<blockquote>
<p>Saturation은 왜 문제가 되는가?
Fig. 1에서 나타나듯이 Epoch에 따라 Training Error Rate를 보면 ReLU가 tanh에 비해 훨씬 빠른 학습 속도를 보여줍니다. 이 이유는 입력값의 절댓값이 커질 수록 출력이 거의 일정해지고 그렇기에 gradient가 매우 작아지며 학습이 느려지게 되기 때문입니다.</p>
</blockquote>
<p>이 ReLU를 통해 학습 속도는 대폭 향상되고, 큰 모델에서도 non-saturating한 활성 함수를 사용하기에 실험을 반복적으로 수행 가능하며, 이러한 깊은 CNN 학습이 현실적인 시간 내에 완료되도록 합니다.</p>
<hr>
<h2 id="ⅱ3-gpu-parallelization">Ⅱ.3. GPU parallelization</h2>
<p>연구진이 사용한 GTX 580 GPU는 3GB의 메모리 크기를 가지고 있기에 1,200,000개의 훈련 데이터를 학습하기에 적합한 Network를 돌리기에는 한 GPU 가지고는 모자랐습니다. 그렇기에 연구진의 선택은 두개의 GPU를 통해 병렬 연산을 진행하는 방식을 선택했습니다. </p>
<p>단순히 Kernel이나 Neuron을 절반으로 나누어 각각 넣는게 아니라 GPU간의 communication을 특정 레이어에서만 전체 연결을 허용합니다. 예를 들어 3번 레이어의 Kernel은 2번 레이어의 모든 Kernel로부터 정보를 받습니다. (이는 두 GPU에서의 값을 모두 받는다는 것을 의미합니다. Communication 발생) </p>
<p>하지만 4번 레이어의 Kernel의 경우 동일 GPU에 있는 3번 레이어의 데이터만 입력값으로 받게 됩니다. 이렇게 함으로써 개별 GPU가 소화할 수 있는 적정한 연산량 안에서의 communication 양을 세밀하게 조절할 수 있게 해줍니다.</p>
<p>이러한 구조는 <strong>Columnar CNN</strong>과 상당히 유사하지만 AlexNet은 column이 독립적이지 않다라는 차이점이 있습니다. </p>
<p>정리하자면 이러한 GPU 병렬화는 개별 GPU로는 수행하지 못하는 양의 연산을 수행하고, 단일 Convolution layer에서 개별 GPU로만 구현했을 때보다 약간 빠르게 수행합니다. </p>
<hr>
<h2 id="ⅱ4-reducing-overfitting">Ⅱ.4. Reducing Overfitting</h2>
<p>물론 모델이 커짐에 따라 너무나도 학습 데이터를 <strong>잘</strong> 학습한 나머지 발생하는 과적합은 필연적이게 됩니다. 이러한 과적합을 연구진은 계속해서 언급했고, 아래의 두가지 전략을 통해 과적합을 줄입니다.</p>
<h3 id="ⅱ41-data-augmentation">Ⅱ.4.1. Data Augmentation</h3>
<p>AlexNet에서는 두가지 방법으로 데이터 증강을 진행합니다. 첫번째는 이미지를 임의로 224 by 224 patch로 잘라내고 좌우 반전을 하는 방식으로 증강을 이루는 것입니다. 이러한 데이터 증강은 훈련 데이터셋의 사이즈를 키우는 방식으로 과적합을 줄여줍니다.</p>
<p>두번째 방법으로는 학습 이미지의 RGB 채널의 강도를 PCA (Primary Component Analysis)를 통해 도출된 principal component를 곱하는 방식으로 조정을 해줍니다. 또한 RGB pixel 값들의 eigenvector와 eigenvalue를 활용해서 단순히 색을 무작위로 변화하는 방식으로 증강한 것이 아니라 현실적인 조명 변화를 흉내내며 증강하게 됩니다.</p>
<p>그래서 <strong>Generating image translations and horizontal reflec
tions</strong>와 <strong>Altering the intensities of the RGB channels in
training images</strong> 두가지 데이터 증강 전략을 통해 과적합을 방지할 수 있었다고 합니다. </p>
<h3 id="ⅱ42-dropout">Ⅱ.4.2. Dropout</h3>
<p>당시 비교적 최근에 소개된 dropout 전략이 AlexNet에 적용된 두번째 과적합 방지 전략입니다. Dropout 전략은 각각의 히든 뉴런의 출력값에 대해서 일정한 확률 (AlexNet에서는 0.5)로 0이 되게끔 설정하는 전략입니다. 이러한 방식으로 누락된 (&quot;Dropped out&quot;) 뉴런들은 다음 레이어에 영향을 미치지 않아 back propagation에도 참여하지 못하게 됩니다. </p>
<p>이렇게 함으로써 학습을 진행할 때마다 구조가 조금씩 다른 네트워크를 학습하는 동시에 weight 값은 공유를 하기에 샘플마다 다른 개별 특징에 초점이 맞춰지는게 아니라 더 일반적인 공통된 특징에 더 집중되어 학습을 진행하게 됩니다. 이렇게 진행함으로써 과적합을 방지할 수 있게 됩니다.</p>
<hr>
<h1 id="ⅲ-limitations--conclusion">Ⅲ. Limitations &amp; Conclusion</h1>
<p>결론적으로 AlexNet은 당시의 GPU 발전과 함께 대규모의 깊은 CNN을 구현했고 그 해 2012년 Large Scale Visual Recognition Challenge (LSVRC)에서 top-5 test error rate로 15.3%로 두번째로 낮았던 26.2%보다 훨씬 뛰어난 성능을 보였습니다.</p>
<p>물론 실험을 단순하게 하기 위해 Unsupervised pre-traing을 사용하지 않았으므로 성능을 더 끌어올릴 수 있는 여지가 있다는 점, 인간의 시각 시스템에 비해서는 아직 너무나도 작은 모델이라는 연구진의 자기 평가가 있었지만 그럼에도 불구하고 깊은 CNN 하나만으로 순수 지도학습에서 당시 최고 성능을 달성했다는 점에서 의미있는 시도였습니다.</p>
<hr>
<blockquote>
<p>오늘은 Computer Vision에서 굵직한 마일 스톤 중 하나인 AlexNet에 대한 논문을 읽어보았습니다. 이번에도 긴 글 읽어주셔서 감사합니다.</p>
<p>의견이나 질문이 있다면 댓글로 남겨주시기 바랍니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1929번 소수 구하기]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1929-%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1929-%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 03 Jan 2026 05:28:39 GMT</pubDate>
            <description><![CDATA[<h1 id="1929-소수-구하기">[1929] 소수 구하기</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-03</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/27a8f1f8-7866-4e7d-ad33-d7fa6eb463b6/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 수학, 에라토스테네스의 체, 구현</li>
<li><strong>요구사항</strong>: 입력된 구간 <code>[n, m]</code> 안에 존재하는 <strong>모든 소수</strong>를 한 줄씩 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>boolean[]</code> : 합성수 여부 (표시 배열)</li>
<li><code>StringBuilder</code> : 출력 누적</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>에라토스테네스의 체 (Sieve of Eratosthenes)</li>
<li>배수 마킹 (Marking multiples)</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>소수 (prime number)</li>
<li>합성수 (composite number)</li>
<li>배수 (multiple)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li><code>0</code>과 <code>1</code>은 소수가 아니므로 먼저 합성수 취급(<code>true</code>)로 표시한다.</li>
<li><code>2</code>부터 시작해, 아직 체크되지 않은 수(소수 후보)를 만나면 그 수의 배수들을 합성수(<code>true</code>)로 표시한다.</li>
<li>최종적으로 <code>arr[i] == false</code>인 수만 출력한다(소수만 남김).<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">arr[0] = arr[1] = true  // 소수 아님(합성수 취급)
for i = 2 .. m:
    if arr[i] == true: continue
    for j = 2 while i*j &lt;= m:
        arr[i*j] = true  // 합성수 표시
&gt;
for i = n .. m:
    if arr[i] == false: print i</code></pre>
<blockquote>
</blockquote>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li><code>0</code>, <code>1</code>은 소수가 아니므로 반드시 제외한다.</li>
<li>boolean 배열의 기본값이 <code>false</code>이므로, “표시되면 합성수(true)” 방식으로 관리한다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static boolean[] arr;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        arr = new boolean[m+1];
        StringBuilder sb = new StringBuilder();
        makeSlieve();

        for(int i = n; i &lt;= m; i++){
            if(!arr[i]) sb.append(i).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }
    public static void makeSlieve(){
        arr[0] = true;
        arr[1] = true;
        for(int i = 2; i &lt; arr.length; i++){
            if(arr[i]) continue;
            for(int j = 2; i*j &lt; arr.length; j++){
                arr[i*j] = true;
            }
        }
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><p><strong>시간 복잡도</strong>: 대략 <code>O(M log log M)</code> 수준 (에라토스테네스의 체)</p>
<ul>
<li>구현상 <code>i*j</code> 형태로 마킹하므로 상수는 커질 수 있으나, 기본 아이디어는 체 방식이다.</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(M)</code> (<code>boolean[m+1]</code>)</p>
</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li><code>boolean</code> 배열의 기본값이 무엇인지 몰라서 혼란이 있었다.</li>
<li>처음에는 <strong>소수만 true</strong>를 기대했지만 실제로는 기본값이 전부 <code>false</code>라서 의도와 반대로 동작하는 문제가 생겼다.</li>
<li>그래서 <strong>합성수만 true로 표시하고, 끝까지 false로 남은 값이 소수</strong>가 되도록 방향을 바꿔 해결했다.</li>
<li>참고 블로그를 통해 boolean 기본값이 <code>false</code>임을 확인했다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>Java에서 <code>boolean[]</code>의 기본값은 <code>false</code>라서, 보통은 <strong>합성수만 true로 마킹</strong>하는 형태가 구현이 편하다.</li>
<li>배수 마킹은 <code>j = i*i</code>부터 시작하거나 $i &lt;= \sqrt{m}$까지만 돌리면 더 빠르게 만들 수 있다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1929">https://www.acmicpc.net/problem/1929</a></li>
<li>참고 블로그/깃허브: <a href="https://nanarin.tistory.com/53">https://nanarin.tistory.com/53</a></li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1978">백준 - 1978번 소수 찾기</a></li>
<li><a href="https://www.acmicpc.net/problem/4948">백준 - 4948번 베르트랑 공준</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/17103">백준 - 17103번 골드바흐 파티션</a></li>
<li><a href="https://www.hackerrank.com/challenges/30-running-time-and-complexity/problem">HackerRank - Day 25: Running Time and Complexity</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 4134번 다음 소수]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-4134%EB%B2%88-%EB%8B%A4%EC%9D%8C-%EC%86%8C%EC%88%98</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-4134%EB%B2%88-%EB%8B%A4%EC%9D%8C-%EC%86%8C%EC%88%98</guid>
            <pubDate>Sat, 03 Jan 2026 05:00:53 GMT</pubDate>
            <description><![CDATA[<h1 id="4134-다음-소수">[4134] 다음 소수</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-03</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/11f8c3ca-2bdf-4021-be60-129f1a3b3227/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 수학, 소수 판정, 브루트포스</li>
<li><strong>요구사항</strong>: 각 테스트케이스마다 입력된 수 <code>n</code> 이상에서 <strong>가장 작은 소수</strong>를 찾아 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>StringBuilder</code> : 여러 줄 출력 누적</li>
<li><code>BufferedReader</code> : 빠른 입력</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>소수 판정(Prime Check)</li>
<li><code>n</code>부터 1씩 증가시키며 첫 소수 탐색</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>소수(prime number)</li>
<li>√n 까지 나눗셈 검사</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>각 입력 <code>target</code>에 대해 <code>current = target</code>으로 시작한다.</li>
<li><code>current</code>가 소수인지 확인하고, 아니면 <code>current++</code> 하며 반복한다.</li>
<li>처음으로 소수 판정을 통과한 <code>current</code>를 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">for i in 1..T:
    current = target
    while true:
        if isPrime(current):
            print current
            break
        current++</code></pre>
<blockquote>
</blockquote>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li><code>0</code>, <code>1</code>은 소수가 아니므로 <code>false</code>로 처리한다.</li>
<li><code>2</code>는 소수이므로 <code>true</code>로 처리한다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long target;
        StringBuilder sb = new StringBuilder();

        for(int i = 0; i &lt; n; i++){
            String s = br.readLine();
            target = Long.parseLong(s);
            long current = target;
            while(true){
                if(isPrime(current)){
                    sb.append(current).append(&quot;\n&quot;);
                    break;
                }
                current++;
            }
        }
        System.out.print(sb);
    }
    public static boolean isPrime(long n){
        if(n == 1 || n == 0) return false;
        if(n == 2) return true;
        for(long i = 2; i*i &lt;= n; i++){
            if(n % i == 0){
                return false;
            }
        }
        return true;
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><p><strong>시간 복잡도</strong>: 최악의 경우 <code>O(K × √M)</code></p>
<ul>
<li><code>K</code> = 소수를 찾을 때까지 증가한 횟수</li>
<li><code>M</code> = 검사 중인 수 크기</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(1)</code> (입력/출력 버퍼 제외)</p>
</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li><code>0</code>, <code>1</code>이 소수가 아니라는 예외를 놓치면 오답이 나기 쉽다.</li>
<li><code>target</code>부터 시작해서 다음 소수를 찾는 반복 구조에서, 소수 판정 함수가 정확해야 한다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>소수 판정은 <code>2</code>부터 <code>√n</code>까지만 나눠보면 된다.</li>
<li>밀러-라빈 테스트를 구현하면 더 효율적인 검증이 가능하지만 아직 어려워 이해하지 못했다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/4134">https://www.acmicpc.net/problem/4134</a></li>
<li>참고 블로그/깃허브: <ul>
<li><a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Testing_against_small_sets_of_bases">Miller–Rabin primality test</a></li>
<li><a href="https://velog.io/@frog_slayer/Miller-Rabin-Primality-Test">[Algo] 밀러-라빈 소수 판별법(Miller-Rabin Primality Test)</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1929">백준 - 1929번 소수 구하기</a></li>
<li><a href="https://www.acmicpc.net/problem/4948">백준 - 4948번 베르트랑 공준</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/17103">백준 - 17103번 골드바흐 파티션</a></li>
<li><a href="https://www.hackerrank.com/challenges/30-running-time-and-complexity/problem">HackerRank - Day 25: Running Time and Complexity</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 2485번 가로수]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2485%EB%B2%88-%EA%B0%80%EB%A1%9C%EC%88%98</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-2485%EB%B2%88-%EA%B0%80%EB%A1%9C%EC%88%98</guid>
            <pubDate>Sat, 03 Jan 2026 03:15:58 GMT</pubDate>
            <description><![CDATA[<h1 id="2485-가로수">[2485] 가로수</h1>
<blockquote>
<p>난이도: ★★★☆☆  •  solved on: 2026-01-03</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/d7fbc60c-606f-44f4-8a17-039d05b1c2ee/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 수학, 최대공약수(GCD)</li>
<li><strong>요구사항</strong>: 기존 가로수 위치를 기준으로 모든 가로수 사이의 간격이 동일해지도록 만들 때, <strong>추가로 심어야 하는 가로수의 개수</strong>를 구해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>long[]</code> : 가로수 좌표 및 간격 저장</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>정렬</li>
<li>유클리드 호제법 (Euclidean Algorithm)</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>인접 간격</li>
<li>간격들의 최대공약수 (GCD)</li>
<li><code>(gap / gcd) - 1</code></li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--현재-가로수의-위치-정보를-저장">방법 1 : 현재 가로수의 위치 정보를 저장</h3>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>가로수 위치를 배열에 저장한다.</li>
<li>인접한 가로수 사이의 간격 배열 <code>gaps</code>를 만든다.</li>
<li>모든 간격의 GCD를 구해 <code>currentGcd</code>로 저장한다.</li>
<li>첫 위치부터 마지막 위치까지 <code>currentGcd</code> 간격으로 하나씩 훑으면서,
기존 가로수가 없는 위치를 카운트한다.<ol start="2">
<li><strong>핵심 로직 흐름</strong><pre><code class="language-text">gaps 생성
gaps 전체 GCD 계산 → currentGcd
start부터 end까지 currentGcd 간격으로 순회
기존 위치에 없으면 count++</code></pre>
<blockquote>
</blockquote>
</li>
<li><strong>한계</strong><blockquote>
</blockquote>
</li>
</ol>
</li>
<li>좌표 범위가 커질 경우, while 문 순회 횟수가 불필요하게 많아질 수 있다.</li>
<li>실제로는 계산으로 바로 구할 수 있는 값을 시뮬레이션으로 처리하고 있다.</li>
</ul>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[] positions = new long[n];
        long[] gaps = new long[n-1];
        long currentGcd = 1;

        for(int i=0;i&lt;n;i++){
            positions[i] = Long.parseLong(br.readLine());
            if(i &gt; 0){
                gaps[i-1] = positions[i] - positions[i-1];
            }
            if(i == n-1){
                break;
            }
        }

        Arrays.sort(positions);

        for(int i=0;i&lt;n-1;i++){
            if(i == 1){
                currentGcd = gcd(gaps[i-1], gaps[i]);
            } else if (i &gt; 1){
                currentGcd = gcd(currentGcd, gaps[i]);
            }
        }

        int i = 0;
        int j = 0;
        long start = positions[0];
        int count = 0;

        while((start + currentGcd * j) &lt;= positions[positions.length-1]){
            if(positions[i] == start + currentGcd * j){
                i ++;
                j ++;
                continue;
            } else {
                count++;
                j ++;
            }
        }
        System.out.println(count);
    }

    public static long gcd(long a,long b){
        while(b != 0){
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }
}</code></pre>
<hr>
<h3 id="방법-2--위치-정보-대신-간격-합산에-집중">방법 2 : 위치 정보 대신 간격 합산에 집중</h3>
<blockquote>
<ol>
<li><strong>핵심 관찰</strong></li>
</ol>
</blockquote>
<ul>
<li>최종적으로 모든 가로수 사이의 간격은
“인접 간격들의 최대공약수(GCD)”가 된다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>계산 방식</strong><blockquote>
</blockquote>
<ul>
<li>어떤 간격 <code>gap</code>이 있고 최종 간격이 <code>gcd</code>라면,</li>
<li>해당 구간은 <code>gap / gcd</code>개의 구간으로 나뉜다.</li>
<li>이미 양 끝에 나무가 있으므로, 추가로 필요한 나무 수는 <code>(gap / gcd) - 1</code>이다.</li>
</ul>
</li>
<li><strong>전체 답</strong><blockquote>
</blockquote>
<ul>
<li>모든 인접 간격에 대해 위 값을 더한 합이다.</li>
</ul>
</li>
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">positions 정렬
모든 인접 gap의 GCD 계산 → g
answer = Σ (gap / g - 1)</code></pre>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    static long gcd(long a, long b) {
        while (b != 0) {
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        long[] pos = new long[N];
        for (int i = 0; i &lt; N; i++) {
            pos[i] = Long.parseLong(br.readLine());
        }

        Arrays.sort(pos);

        long g = pos[1] - pos[0];
        for (int i = 2; i &lt; N; i++) {
            g = gcd(g, pos[i] - pos[i - 1]);
        }

        long answer = 0;
        for (int i = 1; i &lt; N; i++) {
            long gap = pos[i] - pos[i - 1];
            answer += (gap / g) - 1;
        }

        System.out.println(answer);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: $O(N log N + R / G)$ (R: 좌표 범위, G: 최종 간격)</li>
<li><strong>공간 복잡도</strong>: $O(N)$</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: $O(N log N)$</li>
<li><strong>공간 복잡도</strong>: $O(N)$</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>구현 자체는 단순했지만, <strong>시뮬레이션을 수식으로 치환할 수 있다는 발상</strong>에 도달하기 어려웠다.</li>
<li>“모든 위치를 직접 세어보지 않아도 된다”는 점을 떠올리지 못해 시간복잡도 개선 방향을 잡는 데 시간이 걸렸다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>동일 간격을 만드는 문제는 대부분 <strong>간격들의 GCD</strong>로 귀결된다.</li>
<li>좌표 문제에서 “하나씩 훑는 방식”이 보이면, <strong>간격 단위로 계산할 수 있는지</strong> 먼저 의심해보는 것이 좋다. (위치 정보의 필요성에 대한 의심)</li>
<li>추가 개수는 시뮬레이션보다
$Σ (gap / gcd - 1)$ 형태의 수식이 훨씬 안전하고 빠르다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/2485">https://www.acmicpc.net/problem/2485</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/2609">백준 - 2609번 최대공약수와 최소공배수</a></li>
<li><a href="https://www.acmicpc.net/problem/2981">백준 - 2981번 검문</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/14476">백준 - 14476번 최대공약수 하나 빼기</a></li>
<li><a href="https://www.acmicpc.net/problem/1735">백준 - 1735번 분수 합</a>
`</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1934번 최소공배수]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1934%EB%B2%88-%EC%B5%9C%EC%86%8C%EA%B3%B5%EB%B0%B0%EC%88%98</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1934%EB%B2%88-%EC%B5%9C%EC%86%8C%EA%B3%B5%EB%B0%B0%EC%88%98</guid>
            <pubDate>Fri, 02 Jan 2026 09:52:39 GMT</pubDate>
            <description><![CDATA[<h1 id="1934-최소공배수">[1934] 최소공배수</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-02</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/08f29208-415f-4db0-89be-7a9b62d72a6f/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 유클리드 호제법, 수학</li>
<li><strong>요구사항</strong>: 테스트 케이스마다 두 자연수의 <strong>최소공배수(LCM)</strong> 를 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>StringTokenizer</code>, <code>StringBuilder</code></li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>최대공약수 (GCD)</li>
<li>최소공배수 (LCM) 관계식: $LCM(a,b) = (a / GCD(a,b)) * b$</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>유클리드 호제법 (Euclidean Algorithm)</li>
<li>공약수/공배수</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--공통-인수-탐색">방법 1 : 공통 인수 탐색</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>두 수의 공약수를 작은 값부터 나눠가며 공통 인수를 <code>result</code>에 누적한다.</li>
<li>나눠 떨어지면 <code>a</code>, <code>b</code>를 그 인수로 나누고, 나눠 떨어지지 않을 때만 <code>divisor++</code> 한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">result = 1
divisor = 2
while divisor &lt; a or divisor &lt; b:
    if a%divisor==0 and b%divisor==0:
        result *= divisor
        a /= divisor
        b /= divisor
    else:
        divisor++
result *= a*b</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>한 수가 다른 수의 배수인 경우, 더 큰 수가 LCM이므로 바로 출력한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        int a = 0;
        int b = 0;
        int result = 1;

        for(int i = 0; i &lt; n; i++){
            String[] lines = br.readLine().split(&quot; &quot;);

            a = Integer.parseInt(lines[0]);
            b = Integer.parseInt(lines[1]);
            result = 1;

            if(a%b == 0 || b%a == 0){
                sb.append(Math.max(a,b)).append(&quot;\n&quot;);
                continue;
            }
            int min = Math.min(a, b);
            int divisor = 2;
            while(divisor &lt; a || divisor &lt; b){
                if(a%divisor==0 &amp;&amp; b%divisor==0){
                    result *= divisor;
                    a /= divisor;
                    b /= divisor;
                } else {
                    divisor++;
                }
            }

            result *= a * b;
            sb.append(result).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--유클리드-호제법으로-gcd-→-lcm">방법 2 : 유클리드 호제법으로 GCD → LCM</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>최소공배수는 공약수를 “직접” 모두 찾기보다, <strong>최대공약수(GCD)</strong> 만 구하면 바로 계산할 수 있다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">gcd(a,b):
    while b != 0:
        t = a % b
        a = b
        b = t
    return a
&gt;
lcm = a / gcd(a,b) * b</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>곱이 커질 수 있으므로 <code>long</code>을 사용한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

class Main {
    static long gcd(long a, long b) {
        while (b != 0) {
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i &lt; t; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            long a = Long.parseLong(st.nextToken());
            long b = Long.parseLong(st.nextToken());

            long g = gcd(a, b);
            long l = (a / g) * b;   // overflow 방지를 위해 a/g 먼저 수행
            sb.append(l).append(&#39;\n&#39;);
        }
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1-">방법 1 :</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: 최악 O(max(a,b))에 가깝게 커질 수 있다. (작은 약수부터 전부 시도하는 구조)</li>
<li><strong>공간 복잡도</strong>: O(1)</li>
</ul>
<blockquote>
<h4 id="방법-2-">방법 2 :</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(log(min(a,b))) (유클리드 호제법)</li>
<li><strong>공간 복잡도</strong>: O(1)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>처음에는 <code>for (divisor++)</code>로 약수를 검사했는데, 이 방식은 <strong>같은 소수가 여러 번(예: p²)</strong> 나눠지는 경우를 연속으로 반영하기 어렵다는 문제가 있었다.</li>
<li>그래서 “약수를 찾지 못할 때만 ++ 한다”는 방식으로 <code>while</code>로 바꿔 중복 약수를 처리하도록 수정했다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>최소공배수는 공약수를 직접 누적하기보다 <strong>GCD만 구해도</strong> <code>LCM = a / GCD * b</code>로 바로 계산한다.</li>
<li>유클리드 호제법은 구현이 짧고 입력 범위가 커져도 안정적으로 빠르다.</li>
<li>곱셈이 들어가는 문제는 <code>int</code> 대신 <code>long</code>을 먼저 떠올리는 습관이 중요하다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1934">https://www.acmicpc.net/problem/1934</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/2609">백준 - 2609번 최대공약수와 최소공배수</a></li>
<li><a href="https://www.acmicpc.net/problem/2981">백준 - 2981번 검문</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/3036">백준 - 3036번 링</a></li>
<li><a href="https://www.hackerrank.com/challenges/euclid-algorithm/problem">HackerRank - Euclid&#39;s Algorithm</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1269번 대칭 차집합]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1269%EB%B2%88-%EB%8C%80%EC%B9%AD-%EC%B0%A8%EC%A7%91%ED%95%A9</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1269%EB%B2%88-%EB%8C%80%EC%B9%AD-%EC%B0%A8%EC%A7%91%ED%95%A9</guid>
            <pubDate>Thu, 01 Jan 2026 11:22:36 GMT</pubDate>
            <description><![CDATA[<h1 id="1269-대칭-차집합">[1269] 대칭 차집합</h1>
<blockquote>
<p>난이도: ★☆☆☆☆  •  solved on: 2026-01-01</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/b8b50c2f-9215-4dad-a411-8ef2aa17c8b7/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 집합(Set), 해시(Hash)</li>
<li><strong>요구사항</strong>: 두 집합 A, B에 대해 <strong>대칭 차집합</strong> $((A-B) ∪ (B-A))$의 <strong>원소 개수</strong>를 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashSet</code> : 원소 존재 여부를 평균 O(1)로 검사한다.</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>집합 연산의 크기 계산</li>
<li>교집합 개수 세기</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>대칭 차집합 (symmetric difference)</li>
<li>교집합 (intersection)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--합집합을-만들고-교집합-개수만큼-빼기">방법 1 : 합집합을 만들고 교집합 개수만큼 빼기</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>A, B를 각각 <code>HashSet</code>에 넣는다.</li>
<li>A∪B 역할을 하는 <code>setAorB</code>를 만든다.</li>
<li><code>setAorB</code>를 순회하며 A와 B에 모두 있으면(교집합) <code>count++</code> 한다.</li>
<li>최종 답을 <code>|A∪B| - |A∩B|</code> 로 계산한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">setAorB = A ∪ B
count = |A ∩ B|
answer = |setAorB| - count</code></pre>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br =  new BufferedReader(new InputStreamReader(System.in));
        String[] nums = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(nums[0]);
        int m = Integer.parseInt(nums[1]);
        int count = 0;

        HashSet&lt;String&gt; setA = new HashSet&lt;&gt;(n);
        HashSet&lt;String&gt; setB = new HashSet&lt;&gt;(m);
        HashSet&lt;String&gt; setAorB = new HashSet&lt;&gt;();
        StringTokenizer stA = new StringTokenizer(br.readLine());
        StringTokenizer stB = new StringTokenizer(br.readLine());

        while(stA.hasMoreTokens()){
            String temp = stA.nextToken();
            setA.add(temp);
            setAorB.add(temp);
        }

        while(stB.hasMoreTokens()){
            String temp = stB.nextToken();
            setB.add(temp);
            setAorB.add(temp);
        }

        for(String a : setAorB){
            if(setA.contains(a)&amp;&amp;setB.contains(a)){
                count++;
            }
        }

        System.out.println(setAorB.size()-count);
    }
}</code></pre>
<hr>
<h3 id="방법-2--교집합만-세서-n--m---2common로-바로-계산">방법 2 : 교집합만 세서 <code>n + m - 2*common</code>로 바로 계산</h3>
<blockquote>
<ol>
<li>개선 포인트</li>
</ol>
</blockquote>
<ul>
<li>대칭 차집합 크기 = (|A-B| + |B-A|) 이고, 이는 (|A| + |B| - 2|A∩B|) 로 정리된다.</li>
<li>따라서 <strong>합집합 set을 따로 만들 필요가 없다</strong>.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">common = 0
A를 set에 저장
B를 읽으면서 A에 있으면 common++
answer = n + m - 2*common</code></pre>
</li>
<li>추가 개선<blockquote>
</blockquote>
<ul>
<li>입력이 자연수이므로 <code>HashSet&lt;Integer&gt;</code>로 처리하는 편이 의미상 맞고, 변환 비용도 줄인다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        HashSet&lt;Integer&gt; setA = new HashSet&lt;&gt;(n);

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; n; i++) {
            setA.add(Integer.parseInt(st.nextToken()));
        }

        int common = 0;
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; m; i++) {
            int x = Integer.parseInt(st.nextToken());
            if (setA.contains(x)) common++;
        }

        int answer = n + m - 2 * common;
        System.out.println(answer);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M) 평균 (해시 연산 가정)</li>
<li><strong>공간 복잡도</strong>: O(N + M) (<code>setA</code>, <code>setB</code>, <code>setAorB</code> 저장)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M) 평균</li>
<li><strong>공간 복잡도</strong>: O(N) (<code>setA</code>만 유지)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>대칭 차집합 크기는 <strong>$n + m - 2 * |A∩B|$</strong> 로 바로 계산하는 편이 구현과 메모리 측면에서 더 단순하다.</li>
<li>이 문제는 최대 200,000개 원소까지 가능하므로, 이중 루프 대신 <strong>해시 기반 포함 검사</strong>로 풀어야 한다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1269">https://www.acmicpc.net/problem/1269</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/14425">백준 - 14425번 문자열 집합</a></li>
<li><a href="https://www.acmicpc.net/problem/1764">백준 - 1764번 듣보잡</a></li>
<li><a href="https://www.acmicpc.net/problem/7785">백준 - 7785번 회사에 있는 사람</a></li>
<li><a href="https://www.acmicpc.net/problem/10816">백준 - 10816번 숫자 카드 2</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.hackerrank.com/challenges/no-idea/problem">HackerRank - No Idea!</a></li>
<li><a href="https://www.hackerrank.com/challenges/two-strings/problem">HackerRank - Two Strings</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1764번 듣보잡]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1764%EB%B2%88-%EB%93%A3%EB%B3%B4%EC%9E%A1</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1764%EB%B2%88-%EB%93%A3%EB%B3%B4%EC%9E%A1</guid>
            <pubDate>Thu, 01 Jan 2026 04:19:24 GMT</pubDate>
            <description><![CDATA[<h1 id="1764-듣보잡">[1764] 듣보잡</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2026-01-01</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/66eb35ad-5b42-4a47-b6fe-45f0c6193a66/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 해시 (Hash), 문자열, 정렬</li>
<li><strong>요구사항</strong>: 듣도 못한 사람과 보도 못한 사람의 <strong>교집합</strong>을 찾아 <strong>사전식(오름차순)으로 출력해야 한다</strong>.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashMap&lt;String, Integer&gt;</code> / <code>HashSet&lt;String&gt;</code></li>
<li><code>ArrayList&lt;String&gt;</code> : 교집합 결과 저장</li>
<li><code>Collections.sort()</code> : 사전식 정렬</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>해시를 이용한 <strong>중복/존재 여부 체크</strong></li>
<li>교집합 추출 후 정렬</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>교집합 (intersection)</li>
<li>사전식 정렬 (lexicographical order)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--hashmap-카운팅으로-교집합-판별">방법 1 : HashMap 카운팅으로 교집합 판별</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>듣도 못한 사람 N명을 <code>map</code>에 넣고 값 1로 기록한다.</li>
<li>보도 못한 사람 M명을 입력받아 <code>map.getOrDefault(name, 0) + 1</code>로 누적한다.</li>
<li>최종적으로 값이 2인 키가 교집합(듣보잡)이므로 리스트에 담는다.</li>
<li>리스트를 사전식으로 정렬한 뒤 개수와 이름들을 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">map에 N명은 1로 저장
M명을 읽으며 map[name] += 1
map 전체 순회하며 value==2인 key만 list에 저장
list 정렬 후 출력</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>교집합이 0명일 수도 있으므로, 출력 형식을 그대로 유지한다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] s = br.readLine().split(&quot; &quot;);
        int n =  Integer.parseInt(s[0]);
        int m = Integer.parseInt(s[1]);
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        int total = 0;
        StringBuilder sb = new StringBuilder();
        ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();

        for(int i = 0; i &lt; n; i++){
            map.put(br.readLine(), 1);
        }

        for(int i = 0; i &lt; m; i++){
            String name = br.readLine();
            map.put(name, map.getOrDefault(name, 0) + 1);
        }

        for(String key : map.keySet()){
            if(map.get(key)==2){
                total++;
                list.add(key);
            }
        }
        System.out.println(total);

        Collections.sort(list);

        for(int i = 0; i &lt; list.size(); i++){
            sb.append(list.get(i)).append(&quot;\n&quot;);
        }

        System.out.println(sb);

    }
}</code></pre>
<hr>
<h3 id="방법-2--hashset으로-존재-확인-후-교집합만-수집">방법 2 : HashSet으로 존재 확인 후 교집합만 수집</h3>
<blockquote>
<ol>
<li>개선 포인트</li>
</ol>
</blockquote>
<ul>
<li><code>HashMap</code>으로 전체를 카운팅하지 않고, <strong>듣도 목록을 Set에 저장</strong>한 뒤
보도 입력을 읽으면서 <code>set.contains(name)</code>인 경우만 결과 리스트에 추가한다.</li>
<li>교집합 후보만 모으므로, 불필요한 <code>map.keySet()</code> 전체 순회가 사라지고 코드가 더 단순해진다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">set에 N명 저장
M명을 읽으며 set에 있으면 result에 추가
result 정렬 후 출력</code></pre>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        HashSet&lt;String&gt; heard = new HashSet&lt;&gt;(n * 2);
        ArrayList&lt;String&gt; result = new ArrayList&lt;&gt;();

        for (int i = 0; i &lt; n; i++) {
            heard.add(br.readLine());
        }

        for (int i = 0; i &lt; m; i++) {
            String name = br.readLine();
            if (heard.contains(name)) {
                result.add(name);
            }
        }

        Collections.sort(result);

        StringBuilder sb = new StringBuilder();
        sb.append(result.size()).append(&#39;\n&#39;);
        for (String name : result) sb.append(name).append(&#39;\n&#39;);
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M + K log K)
(K = 교집합 크기, 정렬 비용)</li>
<li><strong>공간 복잡도</strong>: O(N + M) 수준 (map에 둘 다 들어갈 수 있음)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M + K log K)</li>
<li><strong>공간 복잡도</strong>: O(N + K) (듣도 set + 결과만 보관)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>처음에는 <code>HashMap</code>으로 <code>false/true</code> 판별 방식도 고려했지만, 사전식 정렬을 해야 해서 결국 카운팅 방식으로 저장한뒤 정렬 로직을 따로 구성했다.</li>
<li><code>Set.contains(value)</code>는 평균적으로 <strong>O(1)</strong>인 것을 까먹었다</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><strong>교집합 문제는 “한 쪽을 Set에 넣고, 다른 쪽을 보면서 contains로 걸러내기”</strong>가 가장 단순한 정석 패턴이다.</li>
<li>사전식 정렬은 결과 리스트만 정렬하면 되므로, 전체 자료구조를 정렬형(<code>TreeSet</code>)으로 유지할 필요는 보통 없다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1764">https://www.acmicpc.net/problem/1764</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/14425">백준 - 14425번 문자열 집합</a></li>
<li><a href="https://www.acmicpc.net/problem/10816">백준 - 10816번 숫자 카드 2</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1764">백준 - 1764번 듣보잡</a> (정렬 대신 출력 포맷/자료구조 변형 연습)</li>
<li><a href="https://www.hackerrank.com/challenges/ctci-ransom-note/problem">HackerRank - Hash Tables: Ransom Note</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 10816번 숫자 카드 2]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-10816%EB%B2%88-%EC%88%AB%EC%9E%90-%EC%B9%B4%EB%93%9C-2</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-10816%EB%B2%88-%EC%88%AB%EC%9E%90-%EC%B9%B4%EB%93%9C-2</guid>
            <pubDate>Wed, 31 Dec 2025 14:32:11 GMT</pubDate>
            <description><![CDATA[<h1 id="10816-숫자-카드-2">[10816] 숫자 카드 2</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2025-12-31</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/6fccfa90-7ece-43a4-97b7-d5b312a0c992/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 해시맵(HashMap), 카운팅(counting)</li>
<li><strong>요구사항</strong>: N개의 정수 카드에서 각 질의 수가 몇 개 있는지 개수를 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashMap&lt;Integer, Integer&gt;</code>: (숫자 → 등장 횟수) 저장</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>빈도수 카운팅 (frequency counting)</li>
<li><code>getOrDefault</code>를 이용한 안전 조회</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>평균 O(1) 조회</li>
<li>중복 원소 개수(count)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--hashmap을-활용한-누적-카운팅">방법 1 : HashMap을 활용한 누적 카운팅</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>N개의 카드를 읽으면서 <code>map</code>에 숫자별 등장 횟수를 누적한다.</li>
<li>M개의 질의를 읽으면서 <code>map.getOrDefault(q, 0)</code>으로 개수를 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">입력 N
카드 N개를 map에 카운팅
&gt;
입력 M
질의 M개에 대해 map[q] 출력 (없으면 0)</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>질의 숫자가 없는 경우 0 출력 (<code>getOrDefault</code>)</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());

        HashMap&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;();
        StringBuilder sb = new StringBuilder();
        String m = br.readLine();
        StringTokenizer st2 = new StringTokenizer(br.readLine());

        while(st.hasMoreTokens()){
            int temp = Integer.parseInt(st.nextToken());
            map.put(temp, map.getOrDefault(temp, 0) + 1);
        }

        while(st2.hasMoreTokens()){
            int q = Integer.parseInt(st2.nextToken());
            sb.append(map.getOrDefault(q, 0)).append(&quot; &quot;);
        }

        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--정렬--이분-탐색">방법 2 : 정렬 + 이분 탐색</h3>
<blockquote>
<ol>
<li>개선 포인트</li>
</ol>
</blockquote>
<ul>
<li>해시맵 풀이도 정답이고 빠르지만, 이 문제는 전형적으로 <strong>정렬 후 lower/upper bound</strong>로 개수를 세는 풀이도 자주 사용한다.</li>
<li>해시맵은 평균 O(1)이지만, 정렬 기반 풀이는 성능이 안정적이고(결정적), 구현 연습 가치가 있다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">카드 배열 정렬
count(x) = upperBound(x) - lowerBound(x)</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>카드에 없는 값이면 두 bound가 같아 0</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    static int lowerBound(int[] arr, int target) {
        int l = 0, r = arr.length; // [l, r)
        while (l &lt; r) {
            int mid = (l + r) &gt;&gt;&gt; 1;
            if (arr[mid] &gt;= target) r = mid;
            else l = mid + 1;
        }
        return l;
    }

    static int upperBound(int[] arr, int target) {
        int l = 0, r = arr.length; // [l, r)
        while (l &lt; r) {
            int mid = (l + r) &gt;&gt;&gt; 1;
            if (arr[mid] &gt; target) r = mid;
            else l = mid + 1;
        }
        return l;
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        int[] cards = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; n; i++) cards[i] = Integer.parseInt(st.nextToken());
        Arrays.sort(cards);

        int m = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine());

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &lt; m; i++) {
            int q = Integer.parseInt(st.nextToken());
            int cnt = upperBound(cards, q) - lowerBound(cards, q);
            sb.append(cnt).append(&#39; &#39;);
        }
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1-hashmap">방법 1 (HashMap)</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M) (평균)</li>
<li><strong>공간 복잡도</strong>: O(N) (서로 다른 값 개수만큼)</li>
</ul>
<blockquote>
<h4 id="방법-2-정렬--이분-탐색">방법 2 (정렬 + 이분 탐색)</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N log N + M log N)</li>
<li><strong>공간 복잡도</strong>: O(N)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><p>이 문제는 <strong>카운팅</strong>이므로 해시맵이 가장 직관적이다.</p>
</li>
<li><p>코딩 테스트 관점에서는</p>
<ul>
<li><strong>빠르게 맞추기</strong>: 해시맵</li>
<li><strong>기본기/안정성 연습</strong>: 정렬 + lower/upper bound
둘 다 정석 범위에 들어간다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/10816">https://www.acmicpc.net/problem/10816</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10815">백준 - 10815번 숫자 카드</a></li>
<li><a href="https://www.acmicpc.net/problem/14425">백준 - 14425번 문자열 집합</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1764">백준 - 1764번 듣보잡</a></li>
<li><a href="https://www.hackerrank.com/challenges/frequency-queries/problem">HackerRank - Frequency Queries</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1620번 나는야 포켓몬 마스터 이다솜]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1620%EB%B2%88-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1620%EB%B2%88-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C</guid>
            <pubDate>Wed, 31 Dec 2025 13:51:48 GMT</pubDate>
            <description><![CDATA[<h1 id="1620-나는야-포켓몬-마스터-이다솜">[1620] 나는야 포켓몬 마스터 이다솜</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2025-12-31</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/f21b1ccf-49b7-4a42-bf93-f512dac1d005/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 해시맵(HashMap), 문자열 처리, 구현</li>
<li><strong>요구사항</strong>: 포켓몬 이름 ↔ 번호를 빠르게 상호 변환하여 질의 M개에 답해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashMap&lt;String, Integer&gt;</code>: 이름 → 번호 조회</li>
<li><code>String[] names</code>: 번호 → 이름 조회 (인덱스 기반)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>양방향 매핑 (bidirectional mapping)</li>
<li>질의가 숫자인지/문자인지 판별하여 분기 처리</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>해시 조회 </li>
<li>문자열 판별 </li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--내-풀이">방법 1 : 내 풀이</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li><code>names[i]</code>에 i번 포켓몬 이름을 저장한다.</li>
<li><code>map</code>에 (이름 → 번호)를 저장한다.</li>
<li>질의가 숫자면 <code>names[index]</code>, 문자면 <code>map.get(name)</code>을 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">입력 N, M
for i=1..N:
    names[i] = pokemon
    map[pokemon] = i
&gt;
for each question:
    if (question is number) -&gt; names[number]
    else -&gt; map[question]</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>없음</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] str = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(str[0]);
        int m = Integer.parseInt(str[1]);
        String[] names = new String[n+1];
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        StringBuilder sb = new StringBuilder();

        for(int i = 1; i &lt;= n; i++){
            String pokemon = br.readLine();
            names[i] = pokemon;
            map.put(pokemon, i);
        }

        for(int i = 0; i &lt; m; i++){
            String question = br.readLine();
            if(question.toUpperCase().equals(question)){
                int index = Integer.parseInt(question);
                sb.append(names[index]).append(&quot;\n&quot;);
            } else {
                sb.append(map.get(question)).append(&quot;\n&quot;);
            }
        }

        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--개선된-최적의-풀이-숫자-판별-로직-교정">방법 2 : 개선된 최적의 풀이 (숫자 판별 로직 교정)</h3>
<blockquote>
<ol>
<li>개선 포인트</li>
</ol>
</blockquote>
<ul>
<li><code>question.toUpperCase().equals(question)</code>는 <strong>“질의가 숫자인지”</strong> 판별하는 조건으로 부적절하다.<blockquote>
</blockquote>
<ul>
<li>숫자 문자열은 대소문자 변화가 없어서 true가 되지만, <strong>이름이 전부 대문자인 경우도</strong> true가 되어 <code>Integer.parseInt()</code>에서 예외가 난다.</li>
<li>하지만 이 문제에서는 예외가 발생하지 않았다. 와우.<blockquote>
</blockquote>
</li>
</ul>
</li>
<li>따라서 <strong>첫 글자가 숫자인지</strong>로 판별하는 방식이 안전하고 빠르다. (<code>Character.isDigit(question.charAt(0))</code>)<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">if Character.isDigit(question.charAt(0)):
    idx = Integer.parseInt(question)
    print names[idx]
else:
    print map.get(question)</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>없음 (문제 조건상 숫자 질의는 항상 올바른 번호로 주어진다)</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        String[] names = new String[n + 1];
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;(n * 2);

        for (int i = 1; i &lt;= n; i++) {
            String pokemon = br.readLine();
            names[i] = pokemon;
            map.put(pokemon, i);
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &lt; m; i++) {
            String q = br.readLine();
            if (Character.isDigit(q.charAt(0))) {
                sb.append(names[Integer.parseInt(q)]).append(&#39;\n&#39;);
            } else {
                sb.append(map.get(q)).append(&#39;\n&#39;);
            }
        }
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M)</li>
<li><strong>공간 복잡도</strong>: O(N)</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: O(N + M)</li>
<li><strong>공간 복잡도</strong>: O(N)</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>“문자열이 숫자인지” 판별할 때 대소문자 변환 비교는 목적에 맞지 않다.</li>
<li>이 문제처럼 입력이 “번호 또는 이름”으로만 주어질 때는 <code>Character.isDigit(첫 글자)</code>가 가장 간단하고 안전하다.</li>
<li>입출력은 <code>BufferedReader + StringTokenizer + StringBuilder</code> 조합이 안정적으로 빠르다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1620">https://www.acmicpc.net/problem/1620</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/14425">백준 - 14425번 문자열 집합</a></li>
<li><a href="https://www.acmicpc.net/problem/7785">백준 - 7785번 회사에 있는 사람</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천):</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1764">백준 - 1764번 듣보잡</a></li>
<li><a href="https://www.acmicpc.net/problem/10816">백준 - 10816번 숫자 카드 2</a></li>
<li><a href="https://www.hackerrank.com/challenges/ctci-ransom-note/problem">HackerRank - Hash Tables: Ransom Note</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 18870번 좌표 압축]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-18870%EB%B2%88-%EC%A2%8C%ED%91%9C-%EC%95%95%EC%B6%95</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-18870%EB%B2%88-%EC%A2%8C%ED%91%9C-%EC%95%95%EC%B6%95</guid>
            <pubDate>Mon, 29 Dec 2025 06:33:19 GMT</pubDate>
            <description><![CDATA[<h1 id="18870-좌표-압축">[18870] 좌표 압축</h1>
<blockquote>
<p>난이도: ★★★☆☆  •  solved on: 2025-12-29</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/0af12ecb-4ac0-4491-95c6-3a9788b47b36/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 정렬, 해시(좌표 압축)</li>
<li><strong>요구사항</strong>: 주어진 수열의 각 값에 대해, <strong>중복 제거 후 정렬했을 때의 인덱스를 원래 순서대로 출력해야 한다.</strong></li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>HashSet</code> : 중복 제거</li>
<li><code>ArrayList</code> : 유니크 값 정렬</li>
<li><code>HashMap</code> : 값 → 압축 인덱스 매핑</li>
<li><code>int[]</code> : 숫자 배열 처리 </li>
<li><code>StringTokenizer</code> : 빠른 입력 파싱 </li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li><strong>좌표 압축 (coordinate compression)</strong>:<ul>
<li>중복 제거 → 정렬 → rank(0..k-1) 부여 → 원본 순서대로 출력</li>
<li>원래 가지고 있던 값들의 의미는 모두 사라지고 오직 순서에 따른 정보만 남게 되는 테크닉</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>value-to-rank mapping     (값→순위 매핑)</li>
<li>unique + sort + map</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--hashset-중복-제거--정렬--hashmap-매핑">방법 1 : HashSet 중복 제거 + 정렬 + HashMap 매핑</h3>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>입력 수열을 문자열 배열로 받고 <code>HashSet</code>으로 중복 제거한다.</li>
<li>유니크 값 리스트(<code>arrList</code>)를 정렬한다.</li>
<li>정렬된 유니크 리스트의 인덱스를 <code>HashMap</code>에 저장해 값→순위 매핑을 만든다.</li>
<li>원본 배열을 순회하면서 <code>map.get(num)</code>으로 순위를 출력한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">arr = 입력(문자열)
set = 중복 제거
arrList = set -&gt; 리스트 변환
arrList 정렬
&gt;
map[value] = 정렬 인덱스
for num in arr:
    출력 map[num]</code></pre>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li>중복이 많아도 <code>HashSet</code>으로 유니크만 정렬하므로 정렬 대상이 줄어든다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.io.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = Integer.parseInt(sc.nextLine());
        String line = sc.nextLine();
        String[] arr = line.split(&quot; &quot;);
        HashSet&lt;String&gt; set = new HashSet&lt;&gt;(Arrays.asList(arr));
        ArrayList&lt;String&gt; arrList = new ArrayList&lt;&gt;(set);

        arrList.sort((a,b)-&gt; {
            return Integer.valueOf(a).compareTo(Integer.valueOf(b));
        });
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        for(int i = 0; i &lt; arrList.size(); i++) {
            map.put(arrList.get(i), i);
        }
        StringBuilder sb = new StringBuilder();
        for(String num : arr){
            sb.append(map.get(num)).append(&quot; &quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--int-배열-복사본-정렬--중복-스캔으로-rank-매핑--빠른-입력">방법 2 : int 배열 복사본 정렬 + 중복 스캔으로 rank 매핑 + 빠른 입력</h3>
<blockquote>
<ol>
<li><strong>핵심 개선 포인트</strong></li>
</ol>
</blockquote>
<ul>
<li><code>Scanner</code>/<code>split</code> 대신 <code>BufferedReader + StringTokenizer</code>로 입력 비용을 줄인다.</li>
<li><code>String</code> 대신 <code>int</code>로 처리해 변환/비교 비용을 줄인다.</li>
<li><code>HashSet</code> 없이도, 정렬된 배열을 한 번 스캔하며 중복을 제거하면서 <code>map</code>을 만들 수 있다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">origin = 원본 int[]
sorted = origin 복사본
sorted 정렬
&gt;
rank = 0
for i in 0..n-1:
    if i==0 or sorted[i] != sorted[i-1]:
        map[sorted[i]] = rank++
for x in origin:
    출력 map[x]</code></pre>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li>음수/큰 수여도 정렬 후 인덱스 기반이므로 문제 없음</li>
<li>출력 마지막 공백은 조건으로 처리</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        int[] origin = new int[n];
        int[] sorted = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; n; i++) {
            int v = Integer.parseInt(st.nextToken());
            origin[i] = v;
            sorted[i] = v;
        }

        Arrays.sort(sorted);

        HashMap&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;(n * 2);
        int rank = 0;
        for (int i = 0; i &lt; n; i++) {
            if (i == 0 || sorted[i] != sorted[i - 1]) {
                map.put(sorted[i], rank++);
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &lt; n; i++) {
            sb.append(map.get(origin[i]));
            if (i + 1 &lt; n) sb.append(&#39; &#39;);
        }
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도: 대략 <code>O(NlongN)</code></strong></p>
<ul>
<li>중복 제거 <code>O(N)</code></li>
<li>유니크 정렬 <code>O(U log U)</code></li>
<li>매핑/출력 <code>O(N)</code></li>
<li>단, <code>Scanner</code>와 <code>split</code>, <code>Integer.valueOf</code> 반복 호출로 체감 시간이 늘 수 있다.</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <strong><code>O(N)</code></strong></p>
</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: <strong><code>O(N log N)</code></strong></li>
<li><strong>공간 복잡도</strong>: <strong><code>O(N)</code></strong></li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li><p>처음 시행착오에서는 정렬된 유니크 리스트에서 순위를 얻기 위해 <code>arrList.indexOf(num)</code> 같은 방식으로 접근했는데, 이 경우 <code>TimeOut</code>이 발생한다. </p>
<ul>
<li>이유는 원소마다 리스트를 선형 탐색하게 되어 전체가 최악 <strong>O(N²)</strong> 로 커질 수 있었기 때문이다.</li>
</ul>
</li>
<li><p>결국 “값의 순위를 찾는 과정”은 반복 탐색이 아니라, <strong><code>HashMap</code>으로 값→순위를 미리 만들어서 즉시 조회</strong>해야 한다는 점이 핵심이었다.</p>
</li>
<li><p>추가로, 입력이 커질 때 <code>Scanner + split</code> 조합이 병목이 될 수 있다는 점도 체감할 수 있는 포인트였다.</p>
</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>좌표 압축은 <strong>정렬 + 매핑</strong>이 핵심이며, 출력 단계는 반드시 <code>O(1)</code> 조회로 끝나야 한다.</li>
<li>대량 입력 문제는 알고리즘이 같아도 <strong>입력 방식(Scanner vs BufferedReader)</strong> 에서 제출 성능 차이가 크게 난다.</li>
<li>숫자는 가능하면 <code>String</code>으로 들고 가지 말고 <code>int</code>로 즉시 파싱하는 편이 깔끔하고 빠르다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/18870">https://www.acmicpc.net/problem/18870</a></li>
<li>참고 블로그/깃허브: 좌표 압축 개념 참고 <a href="https://wikidocs.net/214469">https://wikidocs.net/214469</a> </li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/11650">백준 - 11650번 좌표 정렬하기</a></li>
<li><a href="https://www.acmicpc.net/problem/11651">백준 - 11651번 좌표 정렬하기 2</a></li>
<li><a href="https://www.acmicpc.net/problem/10814">백준 - 10814번 나이순 정렬</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천) :</p>
<ul>
<li><a href="https://www.hackerrank.com/challenges/no-idea/problem">Hackerrank - No Idea!</a></li>
<li><a href="https://www.hackerrank.com/challenges/ctci-comparator-sorting/problem">Hackerrank - Sorting: Comparator</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 10814번 나이순 정렬]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-10814%EB%B2%88-%EB%82%98%EC%9D%B4%EC%88%9C-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-10814%EB%B2%88-%EB%82%98%EC%9D%B4%EC%88%9C-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Mon, 29 Dec 2025 05:32:57 GMT</pubDate>
            <description><![CDATA[<h1 id="10814-나이순-정렬">[10814] 나이순 정렬</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2025-12-29</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/21494a27-ed01-4c27-b1c1-b83233b8c09a/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 정렬 (Sorting), 안정 정렬 (Stable Sort)</li>
<li><strong>요구사항</strong>: 회원을 <strong>나이 오름차순</strong>으로 정렬하되, <strong>나이가 같으면 가입한 순 (입력 순)</strong> 을 유지해서 출력한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>String[][]</code> : 입력순 인덱스, &quot;나이 이름&quot; 원문을 한번에 저장</li>
<li><code>Member[]</code> : 나이/이름을 분리 저장 (커스텀 클래스)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>정렬 (Comparator)</li>
<li><strong>안정 정렬 (stable sort)</strong> 활용</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>안정 정렬 (stable sort), 입력 순서 유지 (input order)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--배열을-활용한-인덱스-저장">방법 1 : 배열을 활용한 인덱스 저장</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>각 입력에 대해 원래 순서를 <code>arr[i][0]</code>에 문자열로 저장한다.</li>
<li>Comparator에서 나이를 비교하고, 같으면 저장한 순서를 비교한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">arr[i] = (i, &quot;age name&quot;)
정렬: age 오름차순, age 같으면 i 오름차순
arr[i][1] 출력</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>나이가 같은 경우 입력 순서 유지</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[][] arr = new String[n][2];
        for(int i = 0; i &lt; n; i++){
            arr[i][0] = i + &quot;&quot;;
            arr[i][1] = br.readLine();
        }
        Arrays.sort(arr, (a, b) -&gt; {
            String[] person1 = a[1].split(&quot; &quot;);
            int person1Age = Integer.parseInt(person1[0]);

            String[] person2 = b[1].split(&quot; &quot;);
            int person2Age = Integer.parseInt(person2[0]);

            if(person1Age != person2Age){
                return person1Age - person2Age;
            }
            return Integer.parseInt(a[0]) - Integer.parseInt(b[0]);
        });

        StringBuilder sb = new StringBuilder();
        for(String[] data : arr){
            sb.append(data[1]).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<hr>
<h3 id="방법-2--입력-파싱-1회--안정-정렬-활용">방법 2 : 입력 파싱 1회 + 안정 정렬 활용</h3>
<blockquote>
<ol>
<li>개선 포인트 (시간/공간)</li>
</ol>
</blockquote>
<ul>
<li>방법 1은 <strong>정렬 비교할 때마다 <code>split()</code>과 <code>parseInt()</code></strong>가 반복되어 비용이 크다.<blockquote>
</blockquote>
</li>
<li>입력 받을 때 <strong>나이/이름을 미리 분리 저장</strong>하면 비교가 매우 가벼워진다.<blockquote>
</blockquote>
</li>
<li>또한 이 문제는 “나이가 같으면 입력 순서 유지”인데, <strong>Java에서 객체 배열 정렬은 안정 정렬</strong>이므로 <strong>나이만 비교해도 입력 순서가 유지</strong>된다.
→ “순서 인덱스”를 따로 저장할 필요가 없어진다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">members[i] = (age, name)
안정 정렬: age 오름차순만 비교
출력: age + &quot; &quot; + name</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>나이가 같을 때는 Comparator가 0을 반환 → 안정 정렬이라 입력 순서 유지</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {

    static class Member {
        int age;
        String name;
        Member(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        Member[] members = new Member[n];
        for (int i = 0; i &lt; n; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int age = Integer.parseInt(st.nextToken());
            String name = st.nextToken();
            members[i] = new Member(age, name);
        }

        Arrays.sort(members, (a, b) -&gt; a.age - b.age); // 나이만 비교 (안정 정렬 활용)

        StringBuilder sb = new StringBuilder();
        for (Member m : members) {
            sb.append(m.age).append(&#39; &#39;).append(m.name).append(&#39;\n&#39;);
        }
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도</strong>: <code>O(N log N * L)</code></p>
<ul>
<li>비교 함수가 호출될 때마다 <code>split()</code> 등 문자열 처리 비용이 반복됨 (<code>L</code> = 문자열 길이)</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(N)</code></p>
</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도</strong>: <code>O(N log N)</code></p>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(N)</code></p>
</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>입력 순서 정보를 어떻게 유지할지 설계하는 부분에서 난감했고, 처음에는 배열에 인덱스를 같이 저장하는 방식으로 해결했다.</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>이 문제의 핵심은 나이 같으면 입력 순서 유지인데, <strong>안정 정렬(stable sort)</strong> 을 활용하면 <strong>인덱스 저장</strong> 자체가 필요 없어질 수 있다.</li>
<li>Comparator 안에서 <code>split()</code> 같은 문자열 파싱을 하면, 정렬 과정에서 같은 문자열을 계속 쪼개게 되어 비효율이 커진다. 따라서 파싱은 최초 저징시 진행하는 것이 좋다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/10814">https://www.acmicpc.net/problem/10814</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/1181">백준 - 1181번 단어 정렬</a></li>
<li><a href="https://www.acmicpc.net/problem/11650">백준 - 11650번 좌표 정렬하기</a></li>
<li><a href="https://www.acmicpc.net/problem/10825">백준 - 10825번 국영수</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/18870">백준 - 18870번 좌표 압축</a></li>
<li><a href="https://www.hackerrank.com/challenges/java-sort/problem">Hackerrank - Sort</a></li>
<li><a href="https://www.hackerrank.com/challenges/java-comparator/problem">Hackerrank - Comparator</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1181번 단어 정렬]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1181%EB%B2%88-%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1181%EB%B2%88-%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Mon, 29 Dec 2025 04:57:00 GMT</pubDate>
            <description><![CDATA[<h1 id="1181-단어-정렬">[1181] 단어 정렬</h1>
<blockquote>
<p>난이도: ★★☆☆☆  •  solved on: 2025-12-29</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/f3f3df9b-c715-4b86-b667-0af592843f7e/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 정렬 (Sorting), 문자열 (String), 중복 제거 (Deduplication)</li>
<li><strong>요구사항</strong>: 단어를 <strong>길이 오름차순</strong>, 길이가 같으면 <strong>사전순 오름차순</strong>으로 정렬하되 <strong>중복 단어는 한 번만</strong> 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>ArrayList&lt;String&gt;</code>: 단어 저장 및 정렬</li>
<li><code>HashSet&lt;String&gt;</code>: 중복 제거(개선 풀이)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>커스텀 정렬 기준 (Comparator)</li>
<li>중복 제거 후 정렬</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>사전순 정렬 (lexicographical order)</li>
<li>다중 키 정렬 (multi-key sort)</li>
<li>해시 기반 중복 제거 (hash-based dedup)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="방법-1--처음-풀이">방법 1 : 처음 풀이</h3>
<blockquote>
<ol>
<li>문제 분해</li>
</ol>
</blockquote>
<ul>
<li>입력을 받으면서 <code>ArrayList</code>에 저장하되, <code>contains</code>로 중복이면 건너뛴다.</li>
<li><code>Comparator</code>로 (길이 → 사전순) 기준으로 정렬한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">for each word:
    if words.contains(word) continue
    words.add(word)
&gt;
words.sort(길이 오름차순, 같으면 사전순)
출력</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li>중복 단어는 <code>contains</code>로 제거</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        ArrayList&lt;String&gt; words = new ArrayList&lt;&gt;();

        for(int i = 0; i &lt; n; i++){
            String word = br.readLine();
            if(words.contains(word)){
                continue;
            }
            words.add(word);
        }

        words.sort(new StringComparator());

        StringBuilder sb = new StringBuilder();
        for(String word : words){
            sb.append(word).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }

    public static class StringComparator implements Comparator&lt;String&gt;{
        @Override
        public int compare(String o1, String o2) {
            if(o1.length() != o2.length()){
                return o1.length() - o2.length();
            }
            if(!o1.equals(o2)){
                return o1.compareTo(o2);
            }
            return 0;
        }
    }
}</code></pre>
<hr>
<h3 id="방법-2--hashset으로-중복-제거-후-정렬">방법 2 : HashSet으로 중복 제거 후 정렬</h3>
<blockquote>
<ol>
<li>개선 포인트 (시간/공간)</li>
</ol>
</blockquote>
<ul>
<li>방법 1의 병목은 <code>words.contains(word)</code>가 <strong>O(N)</strong> 이라서, 전체가 최악 <strong>O(N²)</strong> 로 커질 수 있다는 점이다.</li>
<li>중복 제거는 <code>HashSet</code>이 평균 <strong>O(1)</strong> 이므로 입력 단계가 훨씬 빨라진다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li>핵심 로직 흐름<blockquote>
</blockquote>
<pre><code class="language-text">set에 단어 N개 추가(중복 자동 제거)
set -&gt; list로 변환
list.sort(길이 오름차순, 같으면 사전순)
출력</code></pre>
</li>
<li>예외 처리<blockquote>
</blockquote>
<ul>
<li><code>HashSet</code>이 중복을 자동으로 제거하므로 별도 처리 불필요</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        HashSet&lt;String&gt; set = new HashSet&lt;&gt;();
        for (int i = 0; i &lt; n; i++) {
            set.add(br.readLine()); // 중복 자동 제거
        }

        ArrayList&lt;String&gt; words = new ArrayList&lt;&gt;(set);

        words.sort((a, b) -&gt; {
            if (a.length() != b.length()) return a.length() - b.length();
            return a.compareTo(b);
        });

        StringBuilder sb = new StringBuilder();
        for (String w : words) sb.append(w).append(&#39;\n&#39;);
        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="방법-1">방법 1</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도</strong>: 최악 <code>O(N² + M log M)</code></p>
<ul>
<li>중복 체크 <code>contains</code>가 누적되어 최악 <code>O(N²)</code> 가능</li>
<li><code>M</code> = 중복 제거 후 단어 수</li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(M)</code></p>
</li>
</ul>
<blockquote>
<h4 id="방법-2">방법 2</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도</strong>: 평균 <code>O(N + M log M)</code></p>
<ul>
<li><code>HashSet</code> 삽입 평균 <code>O(1)</code> × N</li>
<li>정렬 <code>O(M log M)</code></li>
</ul>
</li>
<li><p><strong>공간 복잡도</strong>: <code>O(M)</code></p>
</li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><p><strong>중복 제거 + 정렬</strong> 문제는 보통</p>
<ul>
<li>입력 단계: <code>HashSet</code> (또는 <code>TreeSet</code>/정렬 필요 시)로 중복 처리</li>
<li>출력 단계: <code>List</code>로 옮겨 <code>sort</code> 
이 조합이 가장 자주 쓰인다고 한다.</li>
</ul>
</li>
<li><p>비교 함수에서 <code>if(!o1.equals(o2)) return o1.compareTo(o2);</code> 부분은, 길이가 같다면 그냥 <code>return o1.compareTo(o2);</code> 로 충분하다.</p>
</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1181">https://www.acmicpc.net/problem/1181</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10825">백준 - 10825번 국영수</a></li>
<li><a href="https://www.acmicpc.net/problem/11650">백준 - 11650번 좌표 정렬하기</a></li>
<li><a href="https://www.acmicpc.net/problem/10814">백준 - 10814번 나이순 정렬</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/18870">백준 - 18870번 좌표 압축</a></li>
<li><a href="https://www.hackerrank.com/challenges/java-comparator/problem">Hackerrank - Java Comparator</a></li>
<li><a href="https://www.hackerrank.com/challenges/java-sort/problem">Hackerrank - Java Sort</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 11650번 좌표 정렬하기]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-11650%EB%B2%88-%EC%A2%8C%ED%91%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-11650%EB%B2%88-%EC%A2%8C%ED%91%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 29 Dec 2025 04:39:02 GMT</pubDate>
            <description><![CDATA[<h1 id="11650-좌표-정렬하기">[11650] 좌표 정렬하기</h1>
<blockquote>
<p>난이도: ★★☆☆☆   •  solved on: 2025-12-29</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/e4f3e8d9-eb62-454f-a916-9e781bcda788/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 정렬(Sorting), 구현</li>
<li><strong>요구사항</strong>: N개의 점을 <strong>x 오름차순</strong>, x가 같으면 <strong>y 오름차순</strong>으로 정렬해 출력한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>int[][]</code> : 좌표 저장</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>커스텀 정렬 기준 (Comparator) 기반 정렬</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>다중 키 정렬 (multi-key sort), 비교 함수 (comparator)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어">풀이 아이디어</h2>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>커스텀한 <code>Comparator</code>를 활용한다.</li>
<li>(x, y) 좌표쌍을 배열에 저장한다.</li>
<li>정렬 기준을 <code>x -&gt; y</code> 순으로 정의한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">입력 N
좌표 N개를 arr[i][0]=x, arr[i][1]=y에 저장
Arrays.sort(arr, (a,b) -&gt; a[0] 다르면 a[0]-b[0], 같으면 a[1]-b[1])
정렬된 순서대로 출력</code></pre>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li>특별한 예외 처리 없음 (입력 범위 내에서 정렬만 수행)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int[][] arr = new int[n][2];
        for(int i = 0; i &lt; n; i++){
            String[] temp = br.readLine().split(&quot; &quot;);
            int x = Integer.parseInt(temp[0]);
            int y = Integer.parseInt(temp[1]);
            arr[i][0] = x;
            arr[i][1] = y;
        }
        Arrays.sort(arr, new AxisComparator());

        StringBuilder sb = new StringBuilder();
        for(int i = 0; i &lt; n; i++){
            sb.append(arr[i][0] + &quot; &quot;);
            sb.append(arr[i][1] + &quot;\n&quot;);
        }
        System.out.println(sb);
    }

    public static class AxisComparator implements Comparator&lt;int[]&gt;{
        @Override
        public int compare(int[] o1, int[] o2) {
            if(o1[0] != o2[0]){
                return o1[0] - o2[0];
            }
            if(o1[1] != o2[1]){
                return o1[1] - o2[1];
            }
            return 0;
        }

    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<ul>
<li><strong>시간 복잡도</strong>: <code>O(N log N)</code></li>
<li><strong>공간 복잡도</strong>: <code>O(N)</code></li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li>입력 파싱을 <code>split()</code> 대신 <code>StringTokenizer</code>로 바꾸면 메모리/시간이 더 안정적일 수 있다. (특히 N=100,000)</li>
<li>비교에서 <code>o1[0] - o2[0]</code> 대신 <code>Integer.compare(o1[0], o2[0])</code>를 쓰면 오버플로우 위험을 일반적으로 피할 수 있다. (이 문제 범위에서는 큰 문제는 잘 안 생김)</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/11650">https://www.acmicpc.net/problem/11650</a></li>
<li>참고 블로그/깃허브: 없음</li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/11651">백준 - 11651번 좌표 정렬하기 2</a></li>
<li><a href="https://www.acmicpc.net/problem/10814">백준 - 10814번 나이순 정렬</a></li>
<li><a href="https://www.acmicpc.net/problem/18870">백준 - 18870번 좌표 압축</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천)</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10825">백준 - 10825번 국영수</a></li>
<li><a href="https://www.acmicpc.net/problem/1181">백준 - 1181번 단어 정렬</a></li>
<li><a href="https://www.hackerrank.com/challenges/java-comparator/problem">Hackerrank - Java Comparator</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 문제 풀이] 1427번 소트인사이드]]></title>
            <link>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1427%EB%B2%88-%EC%86%8C%ED%8A%B8%EC%9D%B8%EC%82%AC%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@melon-chicken/%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-1427%EB%B2%88-%EC%86%8C%ED%8A%B8%EC%9D%B8%EC%82%AC%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Sun, 28 Dec 2025 01:42:41 GMT</pubDate>
            <description><![CDATA[<h1 id="1427-소트인사이드">[1427] 소트인사이드</h1>
<blockquote>
<p>난이도: ★☆☆☆☆  •  solved on: 2025-12-28</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/melon-chicken/post/7d549fd1-b0cf-4e55-b364-7663f0c695c0/image.png" alt=""></p>
<hr>
<h2 id="문제-요약">문제 요약</h2>
<ul>
<li><strong>문제 유형</strong>: 문자열 처리, 정렬</li>
<li><strong>요구사항</strong>: 입력된 수의 각 자리 숫자를 <strong>내림차순으로 정렬</strong>하여 출력해야 한다.</li>
</ul>
<hr>
<h2 id="사용-개념">사용 개념</h2>
<ol>
<li><p><strong>자료구조</strong></p>
<ul>
<li><code>String</code></li>
<li><code>int[10]</code> (카운팅 배열)</li>
</ul>
</li>
<li><p><strong>알고리즘/기법</strong></p>
<ul>
<li>버블 정렬 형태의 반복 교환</li>
<li>카운팅 정렬</li>
</ul>
</li>
<li><p><strong>핵심 키워드</strong></p>
<ul>
<li>자리수 분해</li>
<li>내림차순 정렬 (descending sort)</li>
<li>문자열 불변 (immutable)</li>
</ul>
</li>
</ol>
<hr>
<h2 id="풀이-아이디어-및-코드">풀이 아이디어 및 코드</h2>
<h3 id="풀이-1--버블-정렬">풀이 1 : 버블 정렬</h3>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>입력 문자열을 순회하며 이웃하는 수 간의 대소관계를 비교한다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">if(String[i] &lt; String[i+1]) 
swap String[i] and String[i+1]</code></pre>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li>각 순회에서 마지막 인덱스에 도달한 경우, 스왑을 점검하지 않고 마친다.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int count = 0;
        while(true){
            count = 0;
            for(int i = 0; i &lt; s.length(); i++){
                if(i == s.length()-1){
                    break;
                }
                if(s.charAt(i) &lt; s.charAt(i+1)){
                    s = swap(i, i+1, s);
                    count++;
                }
            }
            if(count == 0){
                break;
            }
        }
        System.out.println(s);
    }
    public static String swap(int idx1, int idx2, String s){
        char temp = s.charAt(idx1);
        s = s.substring(0, idx1) + s.charAt(idx2) + s.substring(idx1 + 1);
        s = s.substring(0, idx2) + temp + s.substring(idx2 + 1);
        return s;
    }
}</code></pre>
<hr>
<h3 id="풀이-2--카운팅-정렬">풀이 2 : 카운팅 정렬</h3>
<blockquote>
<ol>
<li><strong>문제 분해</strong></li>
</ol>
</blockquote>
<ul>
<li>입력 문자열을 한 글자씩 보며 <code>0~9</code> 등장 횟수를 <code>cnt[10]</code>에 누적한다.</li>
<li><code>9</code>부터 <code>0</code>까지 역순으로, 등장 횟수만큼 출력에 붙이면 내림차순이 된다.<blockquote>
</blockquote>
</li>
</ul>
<ol start="2">
<li><strong>핵심 로직 흐름</strong><blockquote>
</blockquote>
<pre><code class="language-text">cnt[digit]++
for d = 9..0:
    append d, cnt[d]번</code></pre>
</li>
<li><strong>예외 처리</strong><blockquote>
</blockquote>
<ul>
<li>한 자리 입력도 동일하게 처리</li>
</ul>
</li>
</ol>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine().trim();

        int[] cnt = new int[10];
        for (int i = 0; i &lt; s.length(); i++) {
            cnt[s.charAt(i) - &#39;0&#39;]++;
        }

        StringBuilder sb = new StringBuilder();
        for (int d = 9; d &gt;= 0; d--) {
            for (int k = 0; k &lt; cnt[d]; k++) sb.append(d);
        }

        System.out.print(sb);
    }
}</code></pre>
<hr>
<h2 id="시간·공간-복잡도">시간·공간 복잡도</h2>
<blockquote>
<h4 id="풀이-1">풀이 1</h4>
</blockquote>
<ul>
<li><p><strong>시간 복잡도</strong>: 최악 기준 대략 <strong>O(N³)</strong></p>
</li>
<li><p><strong>공간 복잡도</strong>: <strong>O(N)</strong> (문자열이 반복 생성됨)</p>
</li>
</ul>
<blockquote>
<h4 id="풀이-2">풀이 2</h4>
</blockquote>
<ul>
<li><strong>시간 복잡도</strong>: <strong>O(N)</strong></li>
<li><strong>공간 복잡도</strong>: <strong>O(1)</strong> </li>
</ul>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<ul>
<li>특정 인덱스에 있는 <code>char</code>를 교체하는 메소드를 몰라  참고 블로그를 확인하여 <code>swap</code>함수를 구현했었다 (풀이 1)</li>
</ul>
<hr>
<h2 id="배운-점-및-팁">배운 점 및 팁</h2>
<ul>
<li><code>String</code>은 불변(immutable)이라 <code>substring</code> 기반 교체는 매번 새 문자열이 만들어져 비용이 커질 수 있다.</li>
<li>숫자 범위가 <code>0~9</code>로 고정인 경우, 일반 정렬보다 <strong>카운팅 정렬</strong>이 더 간단하고 빠르다.</li>
</ul>
<hr>
<h2 id="참고-및-링크">참고 및 링크</h2>
<ul>
<li>문제 링크: <a href="https://www.acmicpc.net/problem/1427">https://www.acmicpc.net/problem/1427</a></li>
<li>참고 블로그/깃허브: <a href="https://m.blog.naver.com/jjekjjek7/222923433163">https://m.blog.naver.com/jjekjjek7/222923433163</a></li>
</ul>
<hr>
<h2 id="추가-연습-문제">추가 연습 문제</h2>
<ul>
<li><p>비슷한 유형 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/2750">백준 2750번 - 수 정렬하기</a></li>
<li><a href="https://www.acmicpc.net/problem/1181">백준 1181번 - 단어 정렬</a></li>
</ul>
</li>
<li><p>확장 문제 (GPT 추천) :</p>
<ul>
<li><a href="https://www.acmicpc.net/problem/10989">백준 10989번 - 수 정렬하기 3</a></li>
<li><a href="https://www.acmicpc.net/problem/2108">백준 2108번 - 통계학</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>