<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jina_ham.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 24 Jan 2026 14:12:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jina_ham.log</title>
            <url>https://velog.velcdn.com/images/jina_ham/profile/c4782f07-8e36-4de4-970f-d83e61a3cf82/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jina_ham.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jina_ham" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Https 적용후 Post 요청 connection timeout 발생]]></title>
            <link>https://velog.io/@jina_ham/Https-%EC%A0%81%EC%9A%A9%ED%9B%84-Post-%EC%9A%94%EC%B2%AD-connection-timeout-%EB%B0%9C%EC%83%9D</link>
            <guid>https://velog.io/@jina_ham/Https-%EC%A0%81%EC%9A%A9%ED%9B%84-Post-%EC%9A%94%EC%B2%AD-connection-timeout-%EB%B0%9C%EC%83%9D</guid>
            <pubDate>Sat, 24 Jan 2026 14:12:24 GMT</pubDate>
            <description><![CDATA[<h3 id="✅-상황">✅ 상황</h3>
<p>원래 서버에서 http를 사용하고 있다가 https로 배포한 프론트와의 통신을 위해 
가비아 도메인을 ACM을 통해 인증서를 발급받아 새로 https를 적용하였다!</p>
<p>GET 요청으로 health_check를 보냈을 때는 응답이 잘 왔으나 </p>
<h3 id="✅-문제">✅ 문제</h3>
<p>POST 요청을 보내니 connection timeout이 발생하는 것이다.
<img src="https://velog.velcdn.com/images/jina_ham/post/9f71dbeb-88fd-4cfe-ab84-69767ec6fcb4/image.png" alt=""></p>
<p>나는 서버에서 nginx를 활용한 무중단 배포 blue-green을 사용하고 있었다. </p>
<h3 id="✅-원인">✅ 원인</h3>
<ul>
<li>검색해보니</li>
<li>HTTPS 암호화로 인해 패킷이 무거워지거나, nginx의 기본 타임아웃 설정이 데이터가 포함된 POST 요청을 처리하기에 너무 짧은 것이었다.</li>
</ul>
<h3 id="✅-해결">✅ 해결</h3>
<ul>
<li>Nginx 설정 파일 수정하기 (경로 /etc/nginx/conf.d/default.conf)</li>
</ul>
<pre><code>location / {
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    send_timeout 300;
    client_max_body_size 20M;
    ...
}</code></pre><p><img src="https://velog.velcdn.com/images/jina_ham/post/12b0ed6c-0d0c-4708-b3fb-17c0c00e7324/image.png" alt=""></p>
<ul>
<li>위 내용 작성 후 </li>
</ul>
<pre><code>sudo systemctl daemon-reload (서비스 설정 변경 인식)

sudo systemctl reload nginx (Nginx 엔진에 설정 반영)</code></pre><p>다시 postman으로 요청을 보내면 응답이 정상적으로 온다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비전공자도 이해할 수 있는 Docker 입문/실전 (인프런-박재성)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EB%8F%84-%EC%9D%B4%ED%95%B4%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-Docker-%EC%9E%85%EB%AC%B8%EC%8B%A4%EC%A0%84-%EC%9D%B8%ED%94%84%EB%9F%B0-%EB%B0%95%EC%9E%AC%EC%84%B1</link>
            <guid>https://velog.io/@jina_ham/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EB%8F%84-%EC%9D%B4%ED%95%B4%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-Docker-%EC%9E%85%EB%AC%B8%EC%8B%A4%EC%A0%84-%EC%9D%B8%ED%94%84%EB%9F%B0-%EB%B0%95%EC%9E%AC%EC%84%B1</guid>
            <pubDate>Sun, 09 Nov 2025 05:14:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-docker-%EC%9E%85%EB%AC%B8-%EC%8B%A4%EC%A0%84/dashboard">https://www.inflearn.com/course/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-docker-%EC%9E%85%EB%AC%B8-%EC%8B%A4%EC%A0%84/dashboard</a></p>
<p>초기에 개발에 입문 했을 때 사람들이 Docker Docker 하길래 도대체 무슨 기술인지 궁금했다. 백엔드 개발을 시작하면서 다양한 기술들을 배우고 싶었고 인프런 강의를 통해 원하는 기술들을 공부하는데 많은 도움이 되었다. </p>
<p>특히 박재성님 강의 도움을 정말 많이 받았다. 
지금 포스팅은 Docker 강의 후기이지만 정말 많은 강의들을 들었다. 
말그대로 전반적인 강의들은 필요한 액기스만 압축해놓은 거의 하루종일 시간만 투자하면 해당 기술을 배울 수 있다. </p>
<p>무엇보다 박재성님은 수업 자료를 항상 너무나도 꼼꼼하게 노션, pdf 형식으로 모두 제공해주시는게 정말 좋았다. 그만큼 수업에 더 깊이 집중할 수 있었다. </p>
<p>강의는 다음과 같았다.</p>
<ul>
<li>Docker 기본 개념</li>
<li>현업에서 자주 사용하는 Docker CLI 익히기</li>
<li>도커 볼륨을 활용해 데이터 유실 방지하기</li>
<li>Dockerfile 활용해 이미지 직접 만들기 </li>
<li>Docker Compose를 활용해 컨테이너 관리하기</li>
<li>Docker Compose를 활용해 2개 이상의 컨테이너 관리하기</li>
<li>AWS EC2에 서버 배포해보기 </li>
<li>AWS EC2에서 Docker를 활용해 배포해보기</li>
</ul>
<p>박재성님의 다른 강의들은 더 깊숙한 개념을 알고 싶다면 추가적인 학습이 필요한 것들도 있지만 
Docker강의는 이 강의만으로도 Docker를 정복할 수 있을 정도로 강의 구성이 너무 좋았다.</p>
<p>그래서 Docker 강의 덕분에 서버 배포도 이제 수월하게 할 수 있게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 42576번: 완주하지 못한 선수(Java, 해시)]]></title>
            <link>https://velog.io/@jina_ham/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-42576%EB%B2%88-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98Java-%ED%95%B4%EC%8B%9C</link>
            <guid>https://velog.io/@jina_ham/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-42576%EB%B2%88-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98Java-%ED%95%B4%EC%8B%9C</guid>
            <pubDate>Wed, 08 Oct 2025 06:18:17 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">https://school.programmers.co.kr/learn/courses/30/lessons/42576</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/f57526f9-2985-4379-8dbc-9c3d4661c0ec/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b85668073a96ac0cc87e18ffb?pvs=21">해시</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>해당 문제는 HashMap의 key와 value쌍을 이용하여 문제를 해결하는 것이다. 여기서 중요한 것은 예제 3과 같이</p>
<blockquote>
<p>&quot;mislav&quot;는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.</p>
</blockquote>
<p>  동명이인이 있는 경우를 처리하는 것이다.</p>
</li>
<li><p>예제 3에 대해 분석해보자</p>
<ul>
<li>completion 정보를 hashMap에 저장해둔다.</li>
<li>이후 participant를 반복문으로 돌면서 hashMap에 저장된 참가자 정보가 없다면 해당 참가자가 완주하지 못한 것으로 판단하여 해당 참가자의 이름을 반환한다.</li>
</ul>
<ol>
<li>completion 정보 저장<ol>
<li>stanko 삽입 (여기서 value값은 key에 해당하는 이름을 가진 사람수이다.)</li>
</ol>
</li>
</ol>
</li>
</ul>
<pre><code>        | key | value |
        | --- | --- |
        | stanko  | 1 |
    2. ana 삽입


        | key | value |
        | --- | --- |
        | stanko  | 1 |
        | ana  | 1 |
    3. mislav 삽입 .


        | key | value |
        | --- | --- |
        | stanko  | 1 |
        | ana  | 1 |
        | mislav  | 1 |
2. participant 정보다 hashMap에 포함된건지 확인


    | key | value |
    | --- | --- |
    | stanko  | 1 |
    | ana  | 1 |
    | mislav  | 1 |
    1. mislav 확인
        1. 현재 hashMap에는 mislav가 존재한다. 해당 참가자를 확인했으므로 hashMap에서 지워준다.


            | key | value |
            | --- | --- |
            | stanko  | 1 |
            | ana  | 1 |
    2. stanko확인
        1. 현재 hashMap에는 stanko가 존재한다. 해당 참가자를 확인했으므로 hashMap에서 지워준다.


            | key | value |
            | --- | --- |
            | ana  | 1 |
    3. mislav 확인
        1. 현재 hashMap에는 mislav이 존재하지 않는다. 즉 완주자 명단에 존재하지 않는 것이므로 해당 참가자의 이름을 반환한다. </code></pre><h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

class Solution {
    public String solution(String[] participant, String[] completion) {
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        for (String s : completion) {
            if(map.containsKey(s)) {
                Integer value = map.get(s);
                map.put(s, value+1);
            }else {
                map.put(s, 1);
            }
        }

        String answer = &quot;&quot;;
        for (String s : participant) {
            if(map.containsKey(s)) {
                Integer value = map.get(s);
                if(value.equals(1)) map.remove(s);
                else map.put(s, value-1);
            } else {
                answer = s;
                break;
            }
        }

        return answer;
    }

    public static void main(String[] args) {
        String[] participant = {&quot;mislav&quot;, &quot;stanko&quot;, &quot;mislav&quot;, &quot;ana&quot;};
        String[] completion = {&quot;stanko&quot;, &quot;ana&quot;, &quot;mislav&quot;};
        System.out.println(new Solution().solution(participant, completion));
    }
}</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>동명이인을 처리하기 위한 로직이 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 10868번: 최솟값(Java, 트리, 세그먼트 트리)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-10868%EB%B2%88-%EC%B5%9C%EC%86%9F%EA%B0%92Java-%ED%8A%B8%EB%A6%AC-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-10868%EB%B2%88-%EC%B5%9C%EC%86%9F%EA%B0%92Java-%ED%8A%B8%EB%A6%AC-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Mon, 15 Sep 2025 04:56:29 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/10868">https://www.acmicpc.net/problem/10868</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/280e2c04-e358-4394-a583-7b7f99b1bd31/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681f98956c7d72adb5c84?pvs=21">세그먼트 트리</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>이 문제는 구간에 해당하는 최솟값을 구하는 문제이다.</p>
</li>
<li><p>M개의 구간에 대해 최솟값을 각각 구해야 하므로 빠르게 연산을 수행할 수 있는 세그먼트 자료구조를 이용하면 된다.</p>
</li>
<li><p>예제입력 1</p>
</li>
</ul>
<pre><code>10 4
75
30
100
38
50
51
52
20
81
5
1 10
3 5
6 9
8 10</code></pre><ul>
<li><p><strong>1단계, 트리 초기화하기 - 리프노드에 원본 데이터 입력</strong></p>
<ul>
<li><p>2*i ≥ 10 (N : 데이터 개수)를 만족하는 최솟값 I는 4이다.</p>
</li>
<li><p>배열 size = 2*i *2 = 32이다.</p>
</li>
<li><p>시작 리프노트 = 2*i = 16이므로 인덱스 16부터 N개의 데이터를 채운다.</p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/589628c9-0104-4dd5-96ac-224a33662879/image.png" alt=""></p>
</li>
</ul>
</li>
<li><p><strong>2단계, 질의 값 구하기</strong></p>
<ul>
<li><p><strong>세그먼트 트리 index = 주어진 질의 index + 2ⁱ -1를 이용하여 세그먼트 트리용 index로 전환한다.</strong></p>
<p>① start_index % 2 == 1(짝수)일 때 해당 노드를 선택한다.</p>
<p>② end_index % 2 == 0(홀수)일 때 해당 노드를 선택한다. </p>
<p>③ start_index depth 변경 → start_index = (start_index + 1)  / 2 연산을 실행한다.</p>
<p>④ end_index depth 변경 → end_index = (end_index - 1)  / 2 연산을 실행한다. </p>
<p>⑤ ①~④를 반복하다가 end_index &lt; start_index가 되면 종료한다.</p>
</li>
<li><p>예를 들어, 예시입력1에서 3 5는 3번째 데이터에서 5번째 데이터 중 최솟값을 구하는 것이다.</p>
</li>
<li><p>위의 과정을 통해 범위에 속하는 최솟값을 구하면 아래와 같다.</p>
</li>
<li><p>범위 시작 인덱스 = 3 + 2^4 - 1 = 18</p>
</li>
<li><p>범위 끝 인덱스 = 5 + 2^4 - 1 = 20</p>
<aside>
📰

<p>s % 2 == 1 ⇒ 18 % 2 ≠ 1 → 노드 미선택</p>
<p>e % 2 == 0 ⇒ 20 % 2 == 0 → <strong>20번째 노드 선택</strong></p>
<p>s = (s + 1) / 2 ⇒ s = 9</p>
<p>e = (e - 1) / 2 ⇒ e = 9;</p>
<p>s % 2 == 1 ⇒ 9 % 2 ==1 → <strong>9번째 노드 선택</strong></p>
<p>e % 2 == 0 ⇒ 9 % 2 ≠ 0 → 노드 미선택</p>
</aside>

<p>→ 최종적으로 선택된 노드는 20번째 노드와 9번째 노드이다. 여기서 9번째 노드는 18번째와 19번째 노드의 최솟값을 지니고 있으므로 20번째와 9번째 노드를 선택한 것은 18~20번째 노드를 잘 선택한 것과 같다. </p>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/b301cb80-f2f1-43b1-a211-f4c541fcfb98/image.png" alt=""></p>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] tree;
    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());

        int temp = N; // N 보존을 위해 복사
        int treeHeight = 0;
        while(temp != 0) {
            temp /= 2;
            treeHeight++;
        }

        int treeSize = (int) Math.pow(2, treeHeight+1);
        tree = new int[treeSize+1];

        int start_leaf_node_index = (int) Math.pow(2, treeHeight);
        for (int i = start_leaf_node_index; i &lt; start_leaf_node_index + N; i++) {
            tree[i] = Integer.parseInt(br.readLine());
        }

        // 트리 초기화
        setTree(treeHeight);

        for (int i = 0; i &lt; M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            a += (int) Math.pow(2, treeHeight) -1;
            b += (int) Math.pow(2, treeHeight) -1;
            System.out.println(findMin(a, b));
        }

    }

    private static int findMin(int s, int e) {
        // a에서 b까지의 범위 중 최솟값 구하기
        int minValue = Integer.MAX_VALUE;

        while(s &lt;= e) {
            if(s % 2 == 1) {
                minValue = Math.min(minValue, tree[s]);
                s++;
            }
            if(e % 2 == 0) {
                minValue = Math.min(minValue, tree[e]);
                e--;
            }
            s /= 2;
            e /= 2;
        }
        return minValue;
    }

    private static void setTree(int treeHeight) {
        int index = (int) Math.pow(2, treeHeight) - 1;
        for (int i = index; i &gt; 0; i--) {
            tree[i] = Math.min(tree[2*i], tree[2*i+1]);
        }
    }
}</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[
백준 5052번: 전화번호 목록(Java, 트리, 트라이)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-5052%EB%B2%88-%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8-%EB%AA%A9%EB%A1%9DJava-%ED%8A%B8%EB%A6%AC-%ED%8A%B8%EB%9D%BC%EC%9D%B4</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-5052%EB%B2%88-%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8-%EB%AA%A9%EB%A1%9DJava-%ED%8A%B8%EB%A6%AC-%ED%8A%B8%EB%9D%BC%EC%9D%B4</guid>
            <pubDate>Sat, 13 Sep 2025 11:00:46 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/5052">https://www.acmicpc.net/problem/5052</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/77a0e2de-0317-4d66-8ed1-75147a081aaa/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b8566811f9c89fb1f32bbd1bc?pvs=21">트라이</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>해당 문제는 트라이 자료구조를 사용하면 된다.</li>
<li>우선 일관성이 깨지는 경우는 다음과 같이 두가지이다.</li>
</ul>
<h3 id="✍️-첫-번째-911의-prefix-91이-나중에-나오는-경우">✍️ 첫 번째, 911의 prefix 91이 나중에 나오는 경우</h3>
<pre><code class="language-bash">1 // t
2 // n
911
91</code></pre>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/a34f60dc-98a9-4671-b1ca-ee1537f13732/image.png" alt=""></p>
<ul>
<li><p>트라이 자료구조에 911이 있는 상태에서 91을 삽입하는 경우</p>
<ul>
<li><p>91에서 1이 삽입되고 isEnd를 true라고 값을 표시한 후</p>
</li>
<li><p>현재 노드에 대해 자식노드가 존재하는지 탐색한다. 만약 자식이 존재하면 일관성이 벗어나는 것이다.</p>
</li>
<li><p>관련 코드</p>
<pre><code class="language-bash">   if(k == phoneNumber.length()-1) {
                          now.isEnd = true;
                          // 자식이 있으면 일관성 위배
                          for (tNode child : now.next) {
                              if (child != null) {
                                  consistency = false;
                                  break;
                              }
                          }
                      }</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="✍️-두-번째-prefix-911이-먼저-나오고-9112546이-나중에-나오는-경우">✍️ 두 번째, prefix 911이 먼저 나오고 9112546이 나중에 나오는 경우</h3>
<pre><code class="language-bash">2 // t
3 // n
911
97625999
91125426</code></pre>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/34addbf0-d4aa-4e14-b7e8-ee059e19a62d/image.png" alt=""></p>
<ul>
<li><p>트라이 자료구조에서 911은 이미 삽입되어 있고 91125426을 삽입하고 있는 경우</p>
<ul>
<li><p>91125426에서 세번째 숫자인 1을 삽입했는데 해당 숫자를 다 삽입하지 않았는데도 isEnd가 true인 상태이다. 이 경우는 이미 다른 숫자 값이 존재하여 해당 숫자를 prefix로 갖는 경우이므로 일관성에 벗어나는 것이다.</p>
</li>
<li><p>관련 코드</p>
<pre><code class="language-bash">    if(now.isEnd) {
                          consistency = false;
                          break;
                      }</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for (int i = 0; i &lt; t; i++) {
            boolean consistency = true;
            int n = Integer.parseInt(br.readLine()); // 전화번호 수

            tNode root = new tNode(); // 루트노드는 공백으로
            for (int j = 0; j &lt; n; j++) {
                String phoneNumber = br.readLine();
                tNode now = root;
                for (int k = 0; k &lt; phoneNumber.length(); k++) {
                    char ch = phoneNumber.charAt(k);
                    if(now.next[ch - &#39;0&#39;] == null) {
                        now.next[ch - &#39;0&#39;] = new tNode();
                    }
                    if(now.isEnd) {
                        consistency = false;
                        break;
                    }
                    now = now.next[ch - &#39;0&#39;];

                    if(k == phoneNumber.length()-1) {
                        now.isEnd = true;
                        // 자식이 있으면 일관성 위배
                        for (tNode child : now.next) {
                            if (child != null) {
                                consistency = false;
                                break;
                            }
                        }
                    }
                }
            }
            System.out.println(consistency ? &quot;YES&quot; : &quot;NO&quot;);
        }
    }

    static class tNode {
        tNode[] next = new tNode[10];
        boolean isEnd;
    }
}
</code></pre>
<h2 id="☑️-채점-결과---틀림-→-맞음">☑️ 채점 결과 :  틀림 → 맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>아래의 경우를 생각하지 못함</li>
</ul>
<pre><code class="language-bash">1
2
911
91</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1012번: 유기농 양배추(Java, 그래프, DFS, BFS)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1012%EB%B2%88-%EC%9C%A0%EA%B8%B0%EB%86%8D-%EC%96%91%EB%B0%B0%EC%B6%94Java-%EA%B7%B8%EB%9E%98%ED%94%84-DFS-BFS</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1012%EB%B2%88-%EC%9C%A0%EA%B8%B0%EB%86%8D-%EC%96%91%EB%B0%B0%EC%B6%94Java-%EA%B7%B8%EB%9E%98%ED%94%84-DFS-BFS</guid>
            <pubDate>Thu, 11 Sep 2025 01:49:12 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/1012">https://www.acmicpc.net/problem/1012</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/10ba5391-3743-4ec6-a957-640bbb8d0a50/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681caa2e8e5f644438e56?pvs=21">깊이 우선 탐색</a> </p>
<p><a href="https://www.notion.so/268f769b85668049b606fcebdbeb144e?pvs=21">너비 우선 탐색 </a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>해당 문제는 BFS 혹은 DFS 탐색을 진행하면서 탐색이 진행되는 그룹수를 구하면 된다.</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[][] visited;
    static Queue&lt;Cabbage&gt; queue = new LinkedList&lt;&gt;();
    static int T, M, N, K;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        T = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for (int i = 0; i &lt; T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            M = Integer.parseInt(st.nextToken()); // 가로 길이 (열)
            N = Integer.parseInt(st.nextToken()); // 세로 길이 (행)
            K = Integer.parseInt(st.nextToken()); // 배추 위치 개수

            visited = new int[N][M];

            for (int j = 0; j &lt; K; j++) {
                st = new StringTokenizer(br.readLine());
                int x = Integer.parseInt(st.nextToken()); // 가로
                int y = Integer.parseInt(st.nextToken()); // 세로
                visited[y][x] = 1; // 좌표 저장
            }

            queue.clear();
            int count = 0;
            for (int r = 0; r &lt; N; r++) {
                for (int c = 0; c &lt; M; c++) {
                    if (visited[r][c] == 1) {
                        BFS(r, c);
                        count++;
                    }
                }
            }
            System.out.println(count);
        }
    }

    static void BFS(int r, int c) {
        visited[r][c] = 0;
        queue.add(new Cabbage(r, c));

        while (!queue.isEmpty()) {
            Cabbage cur = queue.poll();

            // 상
            if (cur.r - 1 &gt;= 0 &amp;&amp; visited[cur.r - 1][cur.c] == 1) {
                visited[cur.r - 1][cur.c] = 0;
                queue.add(new Cabbage(cur.r - 1, cur.c));
            }
            // 하
            if (cur.r + 1 &lt; N &amp;&amp; visited[cur.r + 1][cur.c] == 1) {
                visited[cur.r + 1][cur.c] = 0;
                queue.add(new Cabbage(cur.r + 1, cur.c));
            }
            // 좌
            if (cur.c - 1 &gt;= 0 &amp;&amp; visited[cur.r][cur.c - 1] == 1) {
                visited[cur.r][cur.c - 1] = 0;
                queue.add(new Cabbage(cur.r, cur.c - 1));
            }
            // 우
            if (cur.c + 1 &lt; M &amp;&amp; visited[cur.r][cur.c + 1] == 1) {
                visited[cur.r][cur.c + 1] = 0;
                queue.add(new Cabbage(cur.r, cur.c + 1));
            }
        }
    }

    static class Cabbage {
        int r, c;
        Cabbage(int r, int c) {
            this.r = r;
            this.c = c;
        }
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>여기서 <code>M</code>은 가로(열), <code>N</code>은 세로(행)</li>
<li>Java에서 배열은 <code>arr[행][열]</code> 구조라서 <code>new int[N][M]</code> 로 만들어야 한다</li>
<li>이전 코드에서는 x=가로, y=세로를 그대로 <code>[x][y]</code>에 넣고 있어서 index가 꼬인다.</li>
</ul>
<p>👉 <code>visited = new int[N][M];</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1647번: 도시 분할 계획(Java, 그래프, 최소신장트리)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1647%EB%B2%88-%EB%8F%84%EC%8B%9C-%EB%B6%84%ED%95%A0-%EA%B3%84%ED%9A%8DJava-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%B5%9C%EC%86%8C%EC%8B%A0%EC%9E%A5%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1647%EB%B2%88-%EB%8F%84%EC%8B%9C-%EB%B6%84%ED%95%A0-%EA%B3%84%ED%9A%8DJava-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%B5%9C%EC%86%8C%EC%8B%A0%EC%9E%A5%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Sun, 31 Aug 2025 07:04:54 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/1647">https://www.acmicpc.net/problem/1647</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/51912706-5667-4e86-b7b3-648691eeb925/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681698e89fe758fa4ee22?pvs=21">최소 신장 트리</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>이 문제는 집(노드)를 길(에지)로 연결했을 때 비용(가중치)가 최소가 되도록 해야 하므로 최소 신장 트리의 개념을 이용하여 문제에 접근해야 한다.</p>
</li>
<li><p>문제에서는 모든 집을 연결하는 것이 아니라 집들을 길로 연결하여 두 개의 마을을 만들어야 한다. 만약 모든 집들을 연결하려면 N-1개의 에지를 사용하면 된다. 하지만 우리는 문제 요구 조건에 따라 두 개의 마을로 나눠야 하므로 N-2개의 에지만 사용하면 된다.</p>
</li>
<li><p>예시 입력 1을 분석해보자</p>
<ul>
<li><p>집의 개수는 7, 길의 개수는 12이고</p>
</li>
<li><p>그 아래 12개의 줄에 집의 번호들과 비용이 작성되어 있다.</p>
</li>
<li><p>최소 신장 트리를 사용하기 위해서는 우선순위 큐를 이용하여 비용이 낮은 순으로 정렬하고</p>
</li>
<li><p>사이클 여부를 판단하기 위해 유니온 파인드를 수행해야 한다.</p>
<pre><code class="language-bash">7 12
1 2 3
1 3 2
3 2 1
2 5 2
3 4 4
7 3 6
5 1 5
1 6 2
6 4 1
6 5 3
4 5 3
6 7 4</code></pre>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/6ab38387-7c25-4ca1-81df-ff66c982a003/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/9bf02cc9-47c3-4f6a-9935-9aa535f7c890/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/87ae9e59-1f12-4847-8ab6-aac53d8d330c/image.png" alt=""></p>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int N, M; // 집의 개수, 길의 개수
    static int[] parent;
    static PriorityQueue&lt;Edge&gt; queue = new PriorityQueue&lt;&gt;();

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

        // 베열 초기화
        parent = new int[N+1];
        for (int i = 1; i &lt;= N; i++) {
            parent[i] = i;
        }

        for (int i = 0; i &lt; M; i++) {
            st = new StringTokenizer(br.readLine());
            int A = Integer.parseInt(st.nextToken());
            int B = Integer.parseInt(st.nextToken());
            int C = Integer.parseInt(st.nextToken());
            queue.add(new Edge(A, B, C));
        }

        int used_Edge = 0;
        int result = 0;
        while (used_Edge &lt; N-2) {
            Edge poll = queue.poll();
            if(find(poll.A) != find(poll.B)) {
                union(poll.A, poll.B);
                used_Edge++;
                result += poll.C;
            }
        }

        System.out.println(result);

    }

    static int find(int a) {
        if(parent[a] == a) return a;
        else return parent[a] = find(parent[a]);
    }

    static void union(int a, int b) {
        a = find(a);
        b = find(b);

        if(a != b) parent[b] = a;
    }

    static class Edge implements Comparable&lt;Edge&gt;{
        int A;
        int B;
        int C;

        Edge(int A, int B, int C) {
            this.A = A;
            this.B = B;
            this.C = C;
        }

        @Override
        public int compareTo(Edge o) {
            return this.C - o.C;
        }
    }

}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 14938번: 서강그라운드(Java, 그래프, 플로이드-워셜)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-14938%EB%B2%88-%EC%84%9C%EA%B0%95%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9CJava-%EA%B7%B8%EB%9E%98%ED%94%84-%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-14938%EB%B2%88-%EC%84%9C%EA%B0%95%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9CJava-%EA%B7%B8%EB%9E%98%ED%94%84-%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C</guid>
            <pubDate>Fri, 29 Aug 2025 04:57:16 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/14938">https://www.acmicpc.net/problem/14938</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/b0caee6c-6668-4266-b6dd-24440d8dccbb/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/241f769b856680de9fd0cac663c6066d?pvs=21">플로이드-워셜⭐</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>해당 문제는 모든 노드간의 최단거리를 계산을 해야하는 문제이므로 플로이드-워션 개념을 사용하여 문제를 풀면된다.</p>
</li>
<li><p>예시입력1을 분석해보면</p>
<pre><code class="language-bash">  5 5 4
  5 7 8 2 3
  1 4 5
  5 2 4
  3 2 3
  1 2 3</code></pre>
<ul>
<li>첫 째줄에는 지역개수(n), 수색범위(m), 길의 개수(r)가 주어진다.</li>
<li>둘째줄에는 1번에서 n번 지역까지 아이템 개수가 주어진다. 이는 별도의 items배열을 생성하여 지역별 아이템 개수를 저장해두고 최대 아이템 개수를 계산할 때 이용하면 된다.</li>
</ul>
</li>
</ul>
<pre><code>    | 1 | 2 | 3 | 4 | 5 |
    | --- | --- | --- | --- | --- |
    | 5 | 7 | 8 | 2 | 3 |
- 이후 r개의 줄에는 길 정보가 주어져있으므로 인접행렬에 저장한다.
    - 단, 양방향으로 지역간 이동이 가능하므로 꼭 양방향으로 거리 정보를 저장한다.</code></pre><ul>
<li><p>최종 출력</p>
<ul>
<li><p>이제 최종 출력은 예은이가 1번~n번 지역에 떨어졌을 때 각각 경우에서 얻을 수 있는 아이템 개수를 계산하고 각 경우에서 얻을 수 있는 아이템 개수가 제일 큰 값을 출력하면 된다.</p>
<pre><code class="language-bash">int max_item = Integer.MIN_VALUE; // 최대 아이템 개수
      for (int i = 1; i &lt;= n; i++) {
          int sum = 0;
          for (int j = 1; j &lt;= n; j++) {
              if(distance[i][j] &lt;= m) {
                  sum += items[j];
              }
          }
          if(max_item &lt; sum) max_item = sum;
      }</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[][] distance; // 거리 배열
    static int[] items; // 지역별 아이템 개수

    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()); // 수색 범위
        int r = Integer.parseInt(st.nextToken()); // 길의 개수

        // 지역별 아이템 개수 저장
        items = new int[n+1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i &lt;= n; i++) {
            items[i] = Integer.parseInt(st.nextToken());
        }

        // 인접 행렬 초기화, i와 j의 값이 같은 곳은 0으로 초기화 나머지는 무한대
        distance = new int[n+1][n+1];
        for (int i = 1; i &lt;= n; i++) {
            for (int j = 1; j &lt;= n; j++) {
                if(i == j) distance[i][j] = 0;
                else distance[i][j] = Integer.MAX_VALUE;
            }
        }

        // 길 정보 입력받기
        for (int i = 0; i &lt; r; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            int l = Integer.parseInt(st.nextToken());
            if(distance[a][b] &gt; l) {
                distance[a][b] = l;
                distance[b][a] = l;
            }
        }

        // 플로이드-워셜을 적용하여 노드간 최단거리 계산하기
        for (int k = 1; k &lt;= n; k++) {
            for (int i = 1; i &lt;= n; i++) {
                for (int j = 1; j &lt;= n; j++) {
                    if(distance[i][k] != Integer.MAX_VALUE &amp;&amp; distance[k][j] != Integer.MAX_VALUE &amp;&amp; distance[i][j] &gt; distance[i][k] + distance[k][j])
                        distance[i][j] = distance[i][k] + distance[k][j];
                }
            }
        }

        int max_item = Integer.MIN_VALUE; // 최대 아이템 개수
        for (int i = 1; i &lt;= n; i++) {
            int sum = 0;
            for (int j = 1; j &lt;= n; j++) {
                if(distance[i][j] &lt;= m) {
                    sum += items[j];
                }
            }
            if(max_item &lt; sum) max_item = sum;
        }

        System.out.println(max_item);
    }

}
</code></pre>
<h2 id="☑️-채점-결과---틀림-→-맞음">☑️ 채점 결과 :  틀림 → 맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>양방향 그래프이므로 거리 정보를 양방향으로 저장했어야 했다.</li>
</ul>
<pre><code class="language-bash">// 처음엔 단방향 저장
if(distance[a][b] &gt; l) {
    distance[a][b] = l;
}

--------&gt;
// 양방향 거리 
if(distance[a][b] &gt; l) {
    distance[a][b] = l;
    distance[b][a] = l;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 7040번: 밥 먹기(Java, 그래프, 벨만-포드)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-7040%EB%B2%88-%EB%B0%A5-%EB%A8%B9%EA%B8%B0Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-7040%EB%B2%88-%EB%B0%A5-%EB%A8%B9%EA%B8%B0Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C</guid>
            <pubDate>Thu, 28 Aug 2025 04:18:57 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/7040">https://www.acmicpc.net/problem/7040</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/b5a5e0e7-afbf-4a83-a626-3c9fdd8d5819/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/241f769b85668001bf01c08b02c9c424?pvs=21">벨만-포드</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>우선 문제 조건에서 1번부터 N번까지의 소들이 번호순대로 줄을 설 때 1번소와 N번 소의 최대 거리를 구해야 한다.</li>
<li>예시 입력 1을 분석해보자</li>
</ul>
<pre><code class="language-bash">4 2 1
1 3 10
2 4 20
2 3 3</code></pre>
<ul>
<li>소는 1번부터 4번까지 4마리가 있고 서로를 좋아하는 소의 쌍은 2쌍, 서로를 싫어하는 소의 쌍은 1쌍이 있다.
1번, 3번 소는 서로를 좋아하므로 최대 10까지만 떨어져 있을 수 있고
2번, 4번 소도 서로를 좋아하므로 최대 20까지만 떨어져 있을 수 있다.
그리고
2번, 3번 소는 서로를 싫어하므로 최소 3만큼 떨어져 있어야 한다.</li>
<li>최종적으로 1번과 4번의 최대 거리를 구해야 하므로 최대한 소들이 서로 떨어져 있게끔 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/332b3e37-4080-44a7-803b-efdaf4be6fd4/image.png" alt=""></p>
<ul>
<li>위 그림을 보면 2와 4가 최대 20만큼 떨어져 있다. 이제 1과 2가 최대한 멀리 떨어져 있을 수록 1과 4사이의 거리가 최대일 것이다.
또한 1과 3이 최대 2만큼 떨어져 있다. 이제 3과 4가 최대한 멀리 떨어져 있을 수록 1과 4사이의 거리가 최대일 것이다. 예시 입력 1에서는
2와 3의 최소 거리가 나와 있으므로 전자를 이용하여 문제를 풀면된다.</li>
<li>따라서 1과 2사이 최대 거리는 7, 2와 4사이 최대 거리는 20이므로 1과 4사이의 최대거리는 27이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/60c4b82e-63b1-49cd-8939-1505f95e2da3/image.png" alt=""></p>
<p>위의 과정을 벨만-포드를 적용하여 이해해보자</p>
<p><strong>에지리스트 – (c1, c2, d)순</strong></p>
<p><strong>(1, 3, 10)</strong></p>
<p><strong>(2, 4, 20)</strong></p>
<p><strong>(2, 3, 3)</strong></p>
<p><strong>1번째 업데이트</strong></p>
<p>(1)(1, 3, 10)</p>
<ul>
<li>1번소의 distance[1] != ∞ =&gt; <strong>distance[3] = 10으로 업데이트</strong></li>
</ul>
<p>(2) (2, 4, 20)</p>
<ul>
<li>2번 소의 distance[2] = ∞ =&gt; 업데이트 불가</li>
</ul>
<p>(3) (2, 3, 3)은 최소 거리이고 우리는 3번 소의 distance[3] = 10 (!= ∞)</p>
<p>라는 것을 이용해서 <strong>distance[2] = distance[3] – 10 = 7로 업데이트</strong></p>
<p><strong>여기서</strong></p>
<p><strong>MD배열에서 distance[c2] != ∞이면 distance[c1] = distance[c2] – d로 계산할 수 있다.</strong></p>
<p><strong>ML배열은 일반적인 벨만포드와 같이 계산하면 된다.</strong></p>
<p><strong>2번째 업데이트</strong></p>
<p>(1)(1, 3, 10)</p>
<ul>
<li>업데이트 진행할 것 없음</li>
</ul>
<p>(2) (2, 4, 20)</p>
<ul>
<li>distance[2] != ∞ <strong>=&gt; distance[4] = distance[2] +20 = 27로 업데이트</strong></li>
</ul>
<p>(3) (2, 3, 3)</p>
<ul>
<li>업데이트 진행할 것 없음</li>
</ul>
<p>최종적으로 한번 더 에지리스트를 순회하여</p>
<ul>
<li>업데이트 되는 값이 있으면 음수사이클이 존재하는 것과 같으므로 줄을 서는 것이 불가 -&gt; <strong>1을 출력</strong></li>
<li>distance[N] = ∞ -&gt; <strong>2출력</strong></li>
<li>줄서는것이 가능 &amp; distance[N] != ∞ -&gt; d<strong>istance[N] 출력</strong></li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] distance; // 거리 배열
    static List&lt;Edge&gt; edge = new ArrayList&lt;&gt;(); // 에지 리스트
    static int N;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        int ML = Integer.parseInt(st.nextToken());
        int MD = Integer.parseInt(st.nextToken());

        distance = new int[N+1];
        Arrays.fill(distance, Integer.MAX_VALUE);

        // ML 입력받기
        for (int i = 0; i &lt; ML; i++) {
            st = new StringTokenizer(br.readLine());
            int c1 = Integer.parseInt(st.nextToken());
            int c2 = Integer.parseInt(st.nextToken());
            int d = Integer.parseInt(st.nextToken());
            edge.add(new Edge(&quot;ML&quot;, c1, c2, d));
        }

        // MD 입력받기
        for (int i = 0; i &lt; MD; i++) {
            st = new StringTokenizer(br.readLine());
            int c1 = Integer.parseInt(st.nextToken());
            int c2 = Integer.parseInt(st.nextToken());
            int d = Integer.parseInt(st.nextToken());
            edge.add(new Edge(&quot;MD&quot;, c1, c2, d));
        }

         // 시작 노드의 거리 배열 값은 0이다.
        distance[1] = 0;
         // N-1번 벨만-포드를 반복한다.
        for (int i = 0; i &lt; N - 1; i++) {
            for (Edge e : edge) {
                if(e.mode.equals(&quot;ML&quot;)) {
                    if(distance[e.c1] != Integer.MAX_VALUE &amp;&amp; distance[e.c2] &gt; distance[e.c1] + e.d) {
                        distance[e.c2] = distance[e.c1] + e.d;
                    }
                } else if(e.mode.equals(&quot;MD&quot;)) {
                    if(distance[e.c2] != Integer.MAX_VALUE &amp;&amp; distance[e.c1] &gt; distance[e.c2] - e.d) {
                        distance[e.c1] = distance[e.c2] - e.d;
                    }
                }
            }
        }

        boolean imPossible = false;
        for (Edge e : edge) {
            if(e.mode.equals(&quot;ML&quot;)) {
                if(distance[e.c1] != Integer.MAX_VALUE &amp;&amp; distance[e.c2] &gt; distance[e.c1] + e.d) {
                    imPossible = true;
                }
            } else if(e.mode.equals(&quot;MD&quot;)) {
                if(distance[e.c2] != Integer.MAX_VALUE &amp;&amp; distance[e.c1] &gt; distance[e.c2] - e.d) {
                    imPossible = true;
                }
            }
        }

        if(imPossible) System.out.println(-1);
        else if(distance[N] == Integer.MAX_VALUE) System.out.println(-2);
        else System.out.println(distance[N]);
    }

    static class Edge {
         String mode;
         int c1;
         int c2;
         int d;

         Edge(String mode, int c1, int c2, int d) {
             this.mode = mode;
             this.c1 = c1;
             this.c2 = c2;
             this.d = d;
         }
    }

}
</code></pre>
<h2 id="☑️-채점-결과---틀림-→-맞음">☑️ 채점 결과 :  틀림 → 맞음</h2>
<ul>
<li>처음에는 벨만-포드는 최단거리 문제인데 여기는 최대 거리를 구하라고 해서 Integer.MAX_VALUE가 아닌 Integer,MIN_VALUE를 사용해서 틀렸다.</li>
</ul>
<aside>
📰

<p>백준 7040번은 &quot;최대 거리&quot;를 직접 구하는 문제가 <strong>아닙니다</strong>.</p>
<p>실제로는 <strong>제약조건을 만족하는 최소 거리</strong>를 구하는 문제예요.</p>
<h3 id="제약-조건">제약 조건</h3>
<ol>
<li><p><strong>ML 도로 (일반 도로)</strong></p>
<ul>
<li><p><code>c2 - c1 ≤ d</code></p>
<p>  즉, <code>c2</code>는 <code>c1</code>보다 최대 d만큼 멀다.</p>
<p>  👉 최단거리 그래프의 &quot;정방향 간선&quot; 제약처럼 동작.</p>
</li>
</ul>
</li>
<li><p><strong>MD 도로 (검문 도로)</strong></p>
<ul>
<li><p><code>c2 - c1 ≥ d</code></p>
<p>  즉, <code>c1</code>은 <code>c2</code>보다 최소 d만큼 가깝다.</p>
<p>  👉 <code>c1 ≤ c2 - d</code>라는 불평등식.</p>
<p>  이 역시 &quot;최단거리 제약&quot; 형태.</p>
</li>
</ul>
</li>
</ol>
<p>즉, 이 문제는</p>
<p><strong>“모든 ML/MD 조건을 만족하는 가장 작은 거리들”</strong>을 찾아야 함 = 최단경로 문제.</p>
</aside>

<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>처음에 문제를 이해하는 것이 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1916번: 최소비용 구하기(Java, 그래프, 다익스트라)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1916%EB%B2%88-%EC%B5%9C%EC%86%8C%EB%B9%84%EC%9A%A9-%EA%B5%AC%ED%95%98%EA%B8%B0Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1916%EB%B2%88-%EC%B5%9C%EC%86%8C%EB%B9%84%EC%9A%A9-%EA%B5%AC%ED%95%98%EA%B8%B0Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BC</guid>
            <pubDate>Tue, 26 Aug 2025 03:40:05 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/1916">https://www.acmicpc.net/problem/1916</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/7609a2a2-0524-4c79-9936-fd47b90f1c42/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681bd90f3f0cc091e1687?pvs=21">다익스트라</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>해당 문제는 최종적으로  출발 도시에서 도착 도시까지 가는데 드는 최소 비용을 출력해야 한다.</li>
<li>최소 비용을 구하기 위해서는 다익스트라를 활용하여 출발 도시에서 모든 노드까지 이르는 최소 거리를 구한 후 마지막 출력에서는 최소 거리 중 도착 도시에 이르는 최소 거리만을 출력하면 된다.
<img src="https://velog.velcdn.com/images/jina_ham/post/1af6d73c-20c9-4d2f-9590-bed98465d204/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/9858cda7-5ac1-4639-9c17-2f4e858920e6/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/f608e5db-1297-4c35-aa3a-55ea310c9ce2/image.png" alt=""></li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static int[] distance; // 최단 거리 저장 배열
    public static boolean[] visited; // 노드 방문 여부 표시
    public static ArrayList&lt;Edge&gt;[] list; // 인접리스트
    public static PriorityQueue&lt;Edge&gt; q = new PriorityQueue&lt;&gt;();

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine()); // 도시 개수
        int M = Integer.parseInt(br.readLine()); // 버스 개수

        distance = new int[N+1];
        visited = new boolean[N+1];
        list = new ArrayList[N+1];

        // 인접리스트 초기화
        for (int i = 1; i &lt;= N; i++) {
            list[i] = new ArrayList&lt;&gt;();
        }

        Arrays.fill(distance, Integer.MAX_VALUE);

        StringTokenizer st;
        // 버스 노선 입력받기
        for (int i = 0; i &lt; M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken()); // 출발 도시
            int b = Integer.parseInt(st.nextToken()); // 도착 도시
            int cost = Integer.parseInt(st.nextToken());
            list[a].add(new Edge(b, cost));
        }

        st = new StringTokenizer(br.readLine());
        int start = Integer.parseInt(st.nextToken());
        int end = Integer.parseInt(st.nextToken());

        distance[start] = 0;
        q.add(new Edge(start, 0));

        while (!q.isEmpty()) {
            Edge current = q.poll();
            int c_v = current.vertex;

            if(visited[c_v]) continue; // 이미 방문한 노드라면 넘어감
            visited[c_v] = true;

            for (Edge edge : list[c_v]) {
                int vertex = edge.vertex;
                int weight = edge.weight;

                if(distance[vertex] &gt; distance[c_v] + weight) {
                    distance[vertex] = distance[c_v] + weight;
                    q.add(new Edge(vertex, distance[vertex]));
                }
            }
        }

        System.out.println(distance[end]);
    }

    static class Edge implements Comparable&lt;Edge&gt; {
        int vertex;
        int weight;

        public Edge(int vertex, int weight) {
            this.vertex = vertex;
            this.weight = weight;
        }
        @Override
        public int compareTo(Edge o) {
            return this.weight - o.weight;
        }
    }

}
</code></pre>
<h2 id="☑️-채점-결과--맞음">☑️ 채점 결과 : 맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 3665번: 최종순위(Java, 그래프, 위상정렬)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-3665%EB%B2%88-%EC%B5%9C%EC%A2%85%EC%88%9C%EC%9C%84Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-3665%EB%B2%88-%EC%B5%9C%EC%A2%85%EC%88%9C%EC%9C%84Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Mon, 25 Aug 2025 05:23:54 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/3665">https://www.acmicpc.net/problem/3665</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/75be55c4-c142-4d8d-bc98-eebac37ebffd/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b8566811f84f3c2bc0699f134?pvs=21">위상 정렬</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>그림을 이용하여 문제를 입력하면 다음과 같다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/77d65483-48bb-4a77-8cc6-f2bbab923874/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/e170a860-f536-41cb-9a83-543b1093298e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/9ca6c145-cbe3-40ad-8eee-20c1b3a77da0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/d7a2df25-04f3-4574-b456-125231386297/image.png" alt=""></p>
<ul>
<li>만약 위상 정렬을 적용하여 노드간의 순서가 잘 출력 될 것 같으면 노드간의 순서를 출력한다.<ul>
<li>하지만, 진입차수가 0인 차수가 없는 경우에는 정렬이 불가하므로 ‘IMPOSSIBLE’을 출력하고</li>
<li>반면, 진입차수가 0인 차수가 존재하지만 여러개 있는 경우 정확한 순위를 매길 수 없으므로 ‘?’를 출력한다.</li>
</ul>
</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] lastRank;
    static ArrayList&lt;ArrayList&lt;Integer&gt;&gt; arrayList; 
    static int[] D;                                 // 진입차수
    static int n;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder out = new StringBuilder();
        int t = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for (int tc = 0; tc &lt; t; tc++) {
            n = Integer.parseInt(br.readLine()); // 팀의 개수

            // 작년 순위
            lastRank = new int[n + 1];
            StringTokenizer st = new StringTokenizer(br.readLine());
            for (int j = 1; j &lt;= n; j++) {
                lastRank[j] = Integer.parseInt(st.nextToken()); j 인덱스에 저장
            }


            arrayList = new ArrayList&lt;&gt;(n + 1);
            for (int j = 0; j &lt;= n; j++) arrayList.add(new ArrayList&lt;&gt;());
            D = new int[n + 1];

            // 작년 순위를 인접리스트에 저장 
            for (int j = 1; j &lt;= n; j++) {
                for (int k = j + 1; k &lt;= n; k++) {
                    int u = lastRank[j]; 
                    int v = lastRank[k]; 
                    arrayList.get(u).add(v);
                    D[v]++;
                }
            }

            // 상대적인 등수가 바뀐 쌍의 수
            int m = Integer.parseInt(br.readLine());
            for (int j = 0; j &lt; m; j++) {
                st = new StringTokenizer(br.readLine());
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
                changeRank(a, b); 
            }

            // 최종 위상 정렬 적용하여 올해 순위 출력하기
            out.append(topologicalSort()).append(&#39;\n&#39;);
        }

        System.out.print(out.toString());
    }

    private static String topologicalSort() {
        Queue&lt;Integer&gt; queue = new ArrayDeque&lt;&gt;();
        for (int i = 1; i &lt;= n; i++) if (D[i] == 0) queue.offer(i);

        List&lt;Integer&gt; order = new ArrayList&lt;&gt;(n);
        boolean ambiguous = false;

        for (int step = 0; step &lt; n; step++) {
            if (queue.isEmpty()) return &quot;IMPOSSIBLE&quot;;  

            if (queue.size() &gt; 1) ambiguous = true;     // 같은 시점의 후보가 2개 이상 → 모호

            int now = queue.poll();
            order.add(now);

            for (int next : arrayList.get(now)) {
                D[next]--;
                if (D[next] == 0) queue.offer(next);
            }
        }

        if (ambiguous) return &quot;?&quot;;

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &lt; n; i++) {
            if (i &gt; 0) sb.append(&#39; &#39;);
            sb.append(order.get(i));
        }
        return sb.toString();
    }

    // a와 b팀의 상대적 순위 바꾸기 
    private static void changeRank(int a, int b) {
        // a -&gt; b 가 있으면 b -&gt; a 로 뒤집기
        if (arrayList.get(a).contains(b)) {
            arrayList.get(a).remove((Integer) b);
            D[b]--;
            arrayList.get(b).add(a);
            D[a]++;
        }
        // 없으면 b -&gt; a가 있다고 보고 a -&gt; b로 뒤집기
        else if (arrayList.get(b).contains(a)) {
            arrayList.get(b).remove((Integer) a);
            D[a]--;
            arrayList.get(a).add(b);
            D[b]++;
        }
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>다양한 예외 상황을 고려하여 코드를 작성하는 것이 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 16562번: 친구비(Java, 그래프, 유니온 파인드)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-16562%EB%B2%88-%EC%B9%9C%EA%B5%AC%EB%B9%84Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8%EB%93%9C</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-16562%EB%B2%88-%EC%B9%9C%EA%B5%AC%EB%B9%84Java-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8%EB%93%9C</guid>
            <pubDate>Mon, 18 Aug 2025 05:12:47 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/16562">https://www.acmicpc.net/problem/16562</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/26c441d6-c746-42e3-831c-2123d145cdaf/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681dfaf2fea52083dbfdb?pvs=21">유니온 파인드</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>우선 예시입력 1, 2를 직접 확인해보면서 풀이를 파악한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/08febaeb-91ea-4e5a-9f8f-ad4121c1eb8d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/db90491e-c5e4-4a12-93e3-11096ce8077a/image.png" alt=""></p>
<ul>
<li>그런데 위의 과정을 구현하고 나면 우리는 parent배열을 통해 집합 관계를 확인할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/3621f36d-143e-4044-ae39-e44f24f8cf6d/image.png" alt=""></p>
<ul>
<li>위 정보를 통해 최소 비용을 계산해야 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/e793239f-a11d-4809-b4a6-b6b9e4b662fa/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/8142e852-5a86-4561-b8cd-577e3f8f17a4/image.png" alt=""></p>
<ul>
<li>즉, 배열을 돌면서 각 친구들의 친구비와 대표 노드의 최소 비용을 비교하면서 최소 비용을 업데이트해가면 된다.</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

public class Main {
    static int[] parent;
    static int[] friendCost;
    static int[] minCost;
    static  int k;
    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()); // 친구 관계수
        k = Integer.parseInt(st.nextToken()); // 가지고 있는 돈

        // 친구 비 저장 배열
        friendCost = new int[N+1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i &lt;= N; i++) {
            friendCost[i] = Integer.parseInt(st.nextToken());
        }

        // 대표 노드 저장 배열
        parent = new int[N+1];
        minCost = new int[N+1];

        // 대표 노드 저장 배열 초기화
        for (int i = 1; i &lt;= N; i++) {
            parent[i] = i;
        }

        // 친구 관계수를 입력받고 해당 친구 번호들에 대해 union 연산을 한다.
        for (int i = 0; i &lt; M; i++) {
            st = new StringTokenizer(br.readLine());
            int f1 = Integer.parseInt(st.nextToken());
            int f2 = Integer.parseInt(st.nextToken());

            if(f2 &lt; f1) union(f2, f1);
            else union(f1, f2);
        }

        // 루트별 최소비 계산
        final int INF = 1_000_000_000;
        int[] minCost = new int[N + 1];
        Arrays.fill(minCost, INF);

        for (int i = 1; i &lt;= N; i++) {
            int r = find(i); // 반드시 find로 루트 보정
            minCost[r] = Math.min(minCost[r], friendCost[i]);
        }

        int sum = 0;
        for (int i = 1; i &lt;= N; i++) {
            if (i == find(i) &amp;&amp; minCost[i] != INF) sum += minCost[i]; // 대표만 합산
        }

        if (sum &lt;= k) System.out.println(sum);
        else System.out.println(&quot;Oh no&quot;);

    }

    public static void union(int a, int b) {
        a = find(a);
        b = find(b);

        if(a != b) {
            parent[b] = a;
        }
    }

    public static int find(int a) {
        // 대표 노드 반환 함수
        if(parent[a] == a) return a;
        else return parent[a] = find(parent[a]);
    }
}
</code></pre>
<h2 id="☑️-채점-결과---틀림-→-맞음">☑️ 채점 결과 :  틀림 → 맞음</h2>
<ul>
<li>집합 관계를 토대로 최소 비용을 계산하는 부분이 틀렸었다.</li>
</ul>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>최소 비용 계산이 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2166번: 다각형의 면적(Java, 기하)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-2166%EB%B2%88-%EB%8B%A4%EA%B0%81%ED%98%95%EC%9D%98-%EB%A9%B4%EC%A0%81Java-%EA%B8%B0%ED%95%98</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-2166%EB%B2%88-%EB%8B%A4%EA%B0%81%ED%98%95%EC%9D%98-%EB%A9%B4%EC%A0%81Java-%EA%B8%B0%ED%95%98</guid>
            <pubDate>Thu, 31 Jul 2025 06:50:53 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/2166">https://www.acmicpc.net/problem/2166</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/4879ba48-86ec-4961-8515-3a08c06d3706/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<h3 id="shoelace-공식shoelace-formula"><strong>Shoelace 공식(Shoelace formula)</strong></h3>
<ul>
<li><strong>2차원 평면에서 다각형의 면적을 구하는 공식</strong>으로, 점들이 <strong>순서대로(시계 혹은 반시계 방향)</strong> 주어졌을 때 사용할 수 있다.</li>
</ul>
<hr>
<h2 id="🥾-shoelace-공식-신발끈-공식">🥾 Shoelace 공식 (신발끈 공식)</h2>
<p>다각형의 꼭짓점이 다음과 같이 순서대로 주어졌다.</p>
<p>$(x1,y1), (x2,y2), (x3,y3), …, (xn,yn)$</p>
<p>이때, 면적 A는 다음과 같은 <strong>신발끈 공식</strong>으로 계산된다.</p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/386b3d5c-380c-4876-9c06-b7112d9ec9e6/image.png" alt=""></p>
<p>단, 여기에 <strong>끝 점의 다음 점</strong>으로 <strong>시작점을 다시 사용해야 하므로</strong>:</p>
<ul>
<li>$xn+1=x1$</li>
<li>$yn+1=y1$이 된다.</li>
</ul>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>해당 문제는 N각형을 나타내는 N개의 좌표가 주어졌을 때 해당 다각형의 면적을 구하는 문제이다.</p>
</li>
<li><p>처음에는 N각형을 쪼개서 삼각형을 만든 다음 각 삼각형의 넓이를 |CCW|/2를 이용하여 구한 다음 다 더해서 N각형의 넓이를 구하려고 했는데 이는 좌표가 시계방향 혹은 반시계 순서대로 주어져야 사용할 수 있다.</p>
</li>
<li><p>N각형의 넓이를 삼각형으로 쪼개지 않고 바로 구할 수 있는 방법은 신발끈 공식을 이용하여 문제를 푼다.</p>
<p>  <img src="https://velog.velcdn.com/images/jina_ham/post/79f75a7a-63c2-4993-99f7-141b3d38ea14/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>단 여기서 주의해야 할 것은 i값이 n일 때 i+1값은 존재하지 않으므로 x1, y1값으로 계산해야 한다는 점이다.</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

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

        Coordinates[] p = new Coordinates[N];
        for (int i = 0; i &lt; N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            p[i] = new Coordinates(a, b);
        }

        double result = 0;
        for (int i = 0; i &lt; N; i++) {
            int j = (i + 1) % N; // 다음 점, 마지막 → 첫 점으로 순환
            result += (long)p[i].x * p[j].y - (long)p[j].x * p[i].y;
        }

        System.out.printf(&quot;%.1f\n&quot;, Math.abs(result) / 2.0);
    }

    public static class Coordinates {
        int x, y;
        Coordinates(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}
</code></pre>
<h2 id="☑️-채점-결과---틀림-→-맞음">☑️ 채점 결과 :  틀림 → 맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>신발끈 공식을 떠올리지 못했다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 9095번: 1, 2, 3 더하기 (Java, 동적 계획법)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-9095%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-Java-%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-9095%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-Java-%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</guid>
            <pubDate>Thu, 31 Jul 2025 04:17:16 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/9095">https://www.acmicpc.net/problem/9095</a></p>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/2609f8a3-2dee-4664-b1c0-66d38b26e5e2/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b8566813dbd46c605b068a3ce?pvs=21">동적 계획법</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>여기서 n의 값은 11보다 작은 양수이다.</li>
<li>n에 대한 경우의 수는 앞의 세 개의 n값들의 합이다.<ul>
<li>왜 앞의 3개의 합이냐면 합을 1, 2, 3의 합으로만 나타낼 수 있기 때문이다.</li>
</ul>
</li>
<li>아래 그림을 보면<ul>
<li>4를 1, 2, 3의 합으로 나타내는 경우의 수는 총 7가지이다.<ul>
<li>1을 나타내는 모든 식에 각각 3을 더해주면 4를 나타내는 식이 된다.</li>
<li>2를 나타내는 모든 식에 각각 2를 더해주면 4를 나타내는 식이 된다.</li>
<li>3을 나타내는 모든 식에 각각 1을 더해주면 4를 나타내는 식이 된다.</li>
</ul>
</li>
<li>따라서 여기서 점화식은 p[i] = p[i-1] + p[i-2] + p[i-3] (i &gt; 3) 이 나온다.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/b3e0b97b-dac8-40da-92c0-8a0d1cf41a58/image.png" alt=""></p>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static int[] dp = new int[11]; // n은 11보다 작다고 했으니 0~10까지

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

        // DP 초기화
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for (int i = 4; i &lt;= 10; i++) {
            dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
        }

        int T = Integer.parseInt(br.readLine());
        for (int i = 0; i &lt; T; i++) {
            int n = Integer.parseInt(br.readLine());
            System.out.println(dp[n]);
        }
    }
}</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2839번: 설탕 배달 (Java, 동적 계획법)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-2839%EB%B2%88-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC-Java-%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-2839%EB%B2%88-%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC-Java-%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</guid>
            <pubDate>Tue, 29 Jul 2025 04:07:15 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/2839">https://www.acmicpc.net/problem/2839</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/adc63dac-845c-4cc6-a0f8-e711053d1be3/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b8566813dbd46c605b068a3ce?pvs=21">동적 계획법</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>이문제는 Nkg으로 설탕의 무게가 주어졌을 때 3kg, 5kg 봉지를 최소 몇개로 만들 수 있느냐에 대한 문제이다.</li>
<li>봉지의 개수를 최소로 하려면 5kg이 최대한 많을 수록 좋다.</li>
<li>따라서 N이 5의 배수에 해당하는 값이라면 N/5의 값이 정답이 될 것이다.</li>
<li>하지만 5의 배수가 아닌 N의 값들이 문제인데</li>
<li>예시 입력 11은 3kg 2개, 5kg 1개, 18은 3kg 1개, 5kg 3개와 같이 3kg의 갯수도 고려해야 한다.</li>
<li>예시 입력 1: N=18<ul>
<li>18은 5의 배수아님 ⇒ 18 - 3 = 15 (count += 1)<ul>
<li>N=18일 때는 5kg만으로 구성되지 않으므로 3kg으로도 채워준다.</li>
<li>18 - 3 = 15, 3kg으로 옮긴 설탕을 제외한 값 15는 5의 배수이다.</li>
<li>15 / 5 = 3이다. (count += 15/5)</li>
<li>최종적으로 3kg 1개, 5kg 3개가 나오는 것이다.</li>
</ul>
</li>
</ul>
</li>
<li>예시 입력 5:N=11<ul>
<li>11은 5의 배수가 아님 ⇒ 11 - 3 = 8 (count += 1)<ul>
<li>11kg에서 일부를 3kg로 채웠는데 남은 값은 여전히 5의 배수가 아님</li>
<li>8 - 3 = 5 (count += 1)</li>
<li>남은 값 5는 5의 배수이므로 5kg 봉지로 채워준다.</li>
<li>5/5 = 1 (count += 5/5)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>⇒ 위의 과정을 재귀함수 형태로 구현한다.</p>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        System.out.println(minNum(N, 0));
    }

    public static int minNum(int N, int count) {
        if (N &lt; 0) return -1;              // 음수면 불가능
        if (N % 5 == 0) return count + N / 5;  // 5로 나눠지면 봉지 수 계산
        return minNum(N - 3, count + 1);   // 3을 빼고 계속 시도
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>재귀함수를 고려하여 알고리즘을 구성해내는게 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS S3 파일 업로드: presigned url을 이용한 파일 업로드 (Spring Boot)]]></title>
            <link>https://velog.io/@jina_ham/AWS-S3-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-presigned-url%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-Spring-Boot</link>
            <guid>https://velog.io/@jina_ham/AWS-S3-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-presigned-url%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-Spring-Boot</guid>
            <pubDate>Tue, 29 Jul 2025 03:07:39 GMT</pubDate>
            <description><![CDATA[<h1 id="✅presigned-url이란">✅Presigned URL이란?</h1>
<ul>
<li>AWS S3와 같은 클라우드 스토리지 서비스에서 제한된 시간 동안 접근 권한을 임시로 부여하는 URL</li>
<li>일반적으로 인증 없이도 파일 업로드/다운로드를 허용하고자 할 때 사용된다.</li>
</ul>
<h2 id="☑️presigned-url의-필요성">☑️Presigned URL의 필요성</h2>
<ul>
<li><p>AWS S3는 기본적으로 인증된 사용자만 파일을 업로드하거나 다운로드할 수 있다. 하지만 다음과 같은 상황에서는 인증 없이도 접근을 허용해야 할 수 있다.</p>
<p>① 사용자가 웹 브라우저나 앱에서 직접 S3에 파일을 업로드하도록 하고 싶을 때</p>
<p>② 특정 사용자에게만 일시적으로 파일 다운로드 권한을 주고 싶을 때</p>
</li>
<li><p>이때 Presigned URL을 사용하면 서버에서 인증된 토큰이 포함된 URL을 생성해서, 클라이언트에게 전달하고, 이 URL을 통해 제한된 시간 동안 안전하게 접근할 수 있다.</p>
</li>
</ul>
<h2 id="☑️-작동방식">☑️ 작동방식</h2>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/b2d494f1-0a73-4ee8-bf27-3e4b5441ee83/image.png" alt=""></p>
<ul>
<li><p>서버가 S3와 통신해서 Presigned URL을 생성한다.</p>
</li>
<li><p>해당 URL에는 S3 버킷 정보, HTTP 메서드(GET/PUT 등), 유효기간, 서명(Signature) 이 포함됩니다.</p>
</li>
<li><p>클라이언트는 이 URL을 통해 S3에 직접 접근할 수 있습니다.</p>
</li>
</ul>
<p>예시: <a href="https://your-bucket.s3.amazonaws.com/file.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=">https://your-bucket.s3.amazonaws.com/file.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=</a>...</p>
<h1 id="✅-aws에서-s3-버킷-생성">✅ AWS에서 S3 버킷 생성</h1>
<h2 id="☑️-s3-버킷-생성">☑️ S3 버킷 생성</h2>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/ac8099ac-cb28-4c75-8824-77972aad4cdf/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/2642a0e1-cbc5-4384-b4e9-f460310cce81/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/ea71f50d-2dfe-4d95-84dc-404e8044aebf/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/d5f2d856-bc1f-4e97-ad33-9a1b29741417/image.png" alt=""></p>
<h2 id="☑️-iam-사용자-생성">☑️ IAM 사용자 생성</h2>
<p><img src="https://velog.velcdn.com/images/jina_ham/post/80ae9ddd-f5c1-4dd0-8bf3-a36294f410dc/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/f7212e19-4134-47d2-b6c4-5b2b0fc50134/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/96bbbda2-8313-4bf6-b92e-7fc6b036c40e/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/977a0c20-cf32-4ad2-b0ee-71b8df0ed06e/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/9f0e82e9-76e2-4df9-8862-2c937937f685/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/7bfef809-350d-4699-95dd-cfa38eec8089/image.png" alt="">
<img src="https://velog.velcdn.com/images/jina_ham/post/1b233749-b11e-40b6-ac04-89fc4e4583b5/image.png" alt="">
<img src="blob:https://velog.io/70e43768-3b63-4b93-8e77-1b33231b2508" alt="업로드중..">
=&gt; 위의 access-key와 secret-key를 꼭 저장해둔다.</p>
<h1 id="✅-코드-구현">✅ 코드 구현</h1>
<h3 id="✔️-buildgradle">✔️ build.gradle</h3>
<pre><code>dependencies {

    ...

    // jdk 의존성 추가 
    implementation &#39;com.amazonaws:aws-java-sdk-s3:1.12.676&#39;

}</code></pre><h3 id="✔️-applicationyml">✔️ application.yml</h3>
<pre><code>cloud:
  aws:
    access-key: [access-key 값]
    secret-key: [secret-key 값]
    region: ap-northeast-2
    bucket: [bucket 이름]
    endpoint: s3.ap-northeast-2.amazonaws.com</code></pre><h3 id="✔️-s3properties">✔️ S3Properties</h3>
<p>=&gt; AWS S3에 접근하기 위한 설정값들을 외부 설정 파일(application.yml 등)로부터 바인딩 받기 위한 역할을 한다.</p>
<pre><code>package com.example.Premind_BE.infra.s3;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = &quot;cloud.aws&quot;)
@Getter
@Setter
public class S3Properties {
    private String accessKey;
    private String secretKey;
    private String region;
    private String bucket;
    private String endpoint;
}
</code></pre><h3 id="✔️-s3-config">✔️ S3 Config</h3>
<p>=&gt; S3Config는 AmazonS3 객체를 생성하고, AWS 인증 정보 및 엔드포인트 설정을 읽어와 구성한 뒤, Spring Bean으로 등록하는 설정 클래스이다.</p>
<pre><code>package com.example.Premind_BE.infra.s3;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(S3Properties.class)
public class S3Config {

    private final S3Properties s3Properties;

    @Bean
    public AmazonS3 amazonS3() {
        AWSCredentials credentials = new BasicAWSCredentials(
                s3Properties.getAccessKey(), s3Properties.getSecretKey());

        AwsClientBuilder.EndpointConfiguration endpointConfig =
                new AwsClientBuilder.EndpointConfiguration(
                        s3Properties.getEndpoint(), s3Properties.getRegion());

        return AmazonS3ClientBuilder.standard()
                .withEndpointConfiguration(endpointConfig)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
    }
}
</code></pre><p><img src="https://velog.velcdn.com/images/jina_ham/post/1485de21-799e-4d2d-be3d-342a50219a13/image.png" alt=""></p>
<ul>
<li>나는 위 화면에서 파일 업로드 항목에 클라이언트가 파일을 놓는 순간 presigned url 발급 요청(클라이언트 역할)이 오면 presigned url을 발급(서버 역할)하고 클라이언트는 발급 받은 presigned url을 이용하여 PUT요청으로 S3에 파일을 업로드 한다.</li>
<li>위 서비스는 하나의 포트폴리오당 하나의 pdf파일만 업로드 가능하기 때문에 하나의 presigned url만 발급하도록 구현하였다. </li>
</ul>
<h2 id="☑️-1단계-presigned-url-발급하기">☑️ 1단계: presigned url 발급하기</h2>
<h3 id="✔️-portfoliocontroller">✔️ PortfolioController</h3>
<pre><code>public class PortfolioController {
    private final PortfolioService portfolioService;

    @Operation(summary = &quot;presigned url 발급&quot;,description = &quot;포트폴리오 업로드 페이지에서 파일을 pdf업로드 항목에 드래그하여 놓는 순간 presigned url을 발급 받고 해당 url로 파일 업로드 진행&quot;)
    @PostMapping(value = &quot;/file-upload&quot;)
    public PresignedUrlResDto generatePresignedUrl() {
        return portfolioService.generatePresignedUrl();
    }
}</code></pre><h3 id="✔️-portfolioservice">✔️ PortfolioService</h3>
<p>=&gt; 파일 이름(fileName)은 사용자의 고유 정보와 UUID 등을 기반으로 생성되며, S3에 저장될 경로 역할을 한다.</p>
<pre><code>public class PortfolioService {
    private final PortfolioRepository portfolioRepository;
    private final UserRepository userRepository;
    private final JobCategoryRepository jobCategoryRepository;
    private final PortfolioQuestionRepository portfolioQuestionRepository;
    private final FileService fileService;
    private final S3Properties s3Properties;
    private final AmazonS3 amazonS3;
    private final JobService jobService;

    public PresignedUrlResDto generatePresignedUrl() {
        Long memberId = getCurrentMember().getId();
        String fileKey = fileService.generateUUID(); // UUID 기반 고유 파일 키 생성
        String fileName = fileService.createFileName(memberId, fileKey); // 사용자 ID와 UUID를 조합한 파일 이름 생성

        // Presigned URL 요청 객체 생성
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
                fileService.createGeneratePresignedUrlRequest(s3Properties.getBucket(), fileName);

        // Presigned URL 생성
        String presignedUrl = amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString();
        return new PresignedUrlResDto(presignedUrl, fileKey);
    }
}</code></pre><h3 id="✔️-fileservice">✔️ FileService</h3>
<p>=&gt; AWS S3 Presigned URL을 생성, 관리, 삭제하는 데 사용되는 유틸리티성 서비스이다.</p>
<pre><code>@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class FileService {
    private final AmazonS3 amazonS3;
    private final S3Properties s3Properties;

    //     파일 구분용 고유 UUID 생성
    public String generateUUID() {
        return UUID.randomUUID().toString();
    }

    // 사용자 ID + UUID 조합으로 S3에 저장할 파일 이름 생성
    public String createFileName(Long memberId, String imageKey) {
        return memberId
                + &quot;/&quot;
                + imageKey;
    }

    //     Presigned URL 생성을 위한 요청 객체 구성
    public GeneratePresignedUrlRequest createGeneratePresignedUrlRequest(String bucket, String fileName) {

        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, fileName)
                .withKey(fileName)
                .withMethod(HttpMethod.PUT)
                .withExpiration(getPresignedUrlExpiration());

        generatePresignedUrlRequest.addRequestParameter(
                Headers.S3_CANNED_ACL, CannedAccessControlList.PublicRead.toString()
        );

        return generatePresignedUrlRequest;
    }

    // Presigned URL의 유효시간(5분) 설정
    public Date getPresignedUrlExpiration() {
        Date expiration = new Date();
        long expTime = expiration.getTime();
        expTime += TimeUnit.MINUTES.toMillis(5);
        expiration.setTime(expTime);

        return expiration;
    }

    // S3에서 해당 파일 삭제
    public void deleteFile(String fileUrl) {
        try {
            String bucket = s3Properties.getBucket();
            String fileKey = extractKeyFromUrl(fileUrl);
            amazonS3.deleteObject(bucket, fileKey);
            log.info(&quot;Deleted file from S3: {}&quot;, fileKey);
        } catch (Exception e) {
            log.error(&quot;Failed to delete file from S3: {}&quot;, fileUrl, e);
        }
    }

    //     전체 URL에서 S3 파일 키만 추출
    private String extractKeyFromUrl(String fileUrl) {
        // presigned URL의 실제 파일 경로만 추출
        URI uri = URI.create(fileUrl);
        return uri.getPath().substring(1); // e.g., &quot;1/f829333c-2e4f-4a38-8eb9-2ee10ae2117e&quot;
    }
}</code></pre><h3 id="✔️-예시-응답">✔️ 예시 응답</h3>
<pre><code>{
    &quot;status&quot;: 200,
    &quot;timestamp&quot;: &quot;2025-07-28T12:35:31.4411101&quot;,
    &quot;success&quot;: true,
    &quot;data&quot;: {
        &quot;presignedUrl&quot;: &quot;[서버측에서 생성한 presigned url 값]&quot;
    }
}</code></pre><h2 id="☑️-2단계-작성완료하여-fileurl과-포트폴리오-작성-내용들을-저장">☑️ 2단계: 작성완료하여 fileUrl과 포트폴리오 작성 내용들을 저장</h2>
<h3 id="✔️-portfoliocontroller-1">✔️ PortfolioController</h3>
<pre><code>@RestController
@RequestMapping(&quot;/portfolio&quot;)
@RequiredArgsConstructor
@Tag(name = &quot;Portfolio API&quot;, description = &quot;포트폴리오 관련 API입니다.&quot;)
public class PortfolioController {
    private final PortfolioService portfolioService;

    ...

    @Operation(summary = &quot;포트폴리오 업로드&quot;,description = &quot;PDF 포트폴리오 파일과 제목/직무/기업/질문답변 정보 등을 함께 업로드합니다.&quot;)
    @PostMapping(value = &quot;/upload&quot;)
    public PortfolioUploadResDto uploadPortfolio(@RequestBody PortfolioUploadReqDto reqDto) {
        return portfolioService.uploadPortfolio(reqDto);
    }
}</code></pre><h3 id="✔️-portfoliouploadreqdto-포트폴리오-업로드-요청-dto">✔️ PortfolioUploadReqDto (포트폴리오 업로드 요청 DTO)</h3>
<p>=&gt; 여기서 요청으로 받는 fileUrl값은 프론트측에서 presignedUrl.split(&#39;?&#39;)[0]을 통해 구한 후 요청으로 보내면 된다!!</p>
<pre><code>package com.example.Premind_BE.domain.portfolio.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = &quot;포트폴리오 업로드를 위한 요청 DTO&quot;)
public class PortfolioUploadReqDto {
    @Schema(description = &quot;선택한 직무의 대분류 ID값&quot;)
    private Long jobMajorId;
    @Schema(description = &quot;선택한 직무의 중분류 ID값&quot;)
    private Long jobMiddleId;
    @Schema(description = &quot;선택한 직무의 소분류 ID값&quot;)
    private Long jobMinorId;
    @Schema(description = &quot;포트폴리오 제목&quot;)
    private String title;
    @Schema(description = &quot;포트폴리오 지원 기업명&quot;)
    private String company;
    @Schema(description = &quot;포트폴리오 질문-답변 항목 리스트&quot;)
    private List&lt;PortfolioSectionReqDto&gt; qaList;
    @Schema(description = &quot;업로드된 S3 파일 경로, presignedUrl.split(&#39;?&#39;)[0] 값이다.&quot;)
    private String fileUrl;

}
</code></pre><h3 id="✔️-portfolioservice-1">✔️ PortfolioService</h3>
<pre><code>public PortfolioUploadResDto uploadPortfolio(PortfolioUploadReqDto reqDto) {
        List&lt;JobCategory&gt; jobCategories = jobService.findJobCategory(reqDto.getJobMajorId(), reqDto.getJobMiddleId(), reqDto.getJobMinorId());
        Portfolio portfolio = Portfolio.builder()
                .user(getCurrentMember())
                .jobMajor(jobCategories.get(0))
                .jobMiddle(jobCategories.get(1))
                .jobMinor(jobCategories.get(2))
                .title(reqDto.getTitle())
                .company(reqDto.getCompany())
                .filePath(reqDto.getFileUrl())
                .createdDate(LocalDateTime.now())
                .build();

        log.info(&quot;Portfolio created with title: {}&quot;, portfolio.getTitle());

        List&lt;PortfolioSectionReqDto&gt; sectionList = reqDto.getQaList();
        for (int i = 0; i &lt; sectionList.size(); i++) {
            PortfolioSectionReqDto section = sectionList.get(i);
            PortfolioSection resumeSection = PortfolioSection.builder()
                    .sequence(i + 1)
                    .question(section.getQuestion())
                    .answer(section.getAnswer())
                    .build();
            portfolio.addSection(resumeSection);
        }

        portfolioRepository.save(portfolio);

        return new PortfolioUploadResDto(portfolio.getId());
    }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1010번: 다리 놓기 (Java, 조합)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1010%EB%B2%88-%EB%8B%A4%EB%A6%AC-%EB%86%93%EA%B8%B0-Java-%EC%A1%B0%ED%95%A9</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1010%EB%B2%88-%EB%8B%A4%EB%A6%AC-%EB%86%93%EA%B8%B0-Java-%EC%A1%B0%ED%95%A9</guid>
            <pubDate>Sun, 27 Jul 2025 03:01:57 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/1010">https://www.acmicpc.net/problem/1010</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/14af459b-4a82-46c0-a8d5-cb820ca05b14/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856681ffbdd5ffebd6ecd925?pvs=21">조합</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>문제 조건에 N ≤ M 조건에 따라 서쪽 사이트가 동쪽 사이트보다 작거나 같다는 것을 알 수 있다. 최대한 많은 다리를 지으려면 서쪽 사이트는 모두 다리로 연결될 수 밖에 없다. 그렇다면 서쪽 사이트의 개수만큼 동쪽에 있는 사이트를 선택해서 이어 주면 된다.</li>
<li>즉, 연산이 최종적으로 MCN이 되는 것이다.<ul>
<li>예시 입력1 2C2 = 1</li>
<li>예시 입력2 5C1 = 5</li>
<li>예시 입력3 29C15 = 67863915</li>
</ul>
</li>
<li>조합 점화식<ul>
<li>nCr = nCr-1 + n-1Cr-1 을 사용하여 코드를 작성한다.</li>
</ul>
</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine()); // 테스트 케이스 개수

        for (int i = 0; i &lt; T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());

            System.out.println(combination(N, M));
        }
    }

    public static int combination(int N, int M) {
        int[][] p = new int[M+1][M+1];

        for (int i = 0; i &lt;= M; i++) {
            for (int j = 0; j &lt;= i; j++) {
                if(j == 0 || j == i)
                    p[i][j] = 1;
                else p[i][j] = p[i-1][j] + p[i-1][j-1];
            }
        }
        return p[M][N];
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 14565번: 역원(Inverse) 구하기 (Java, 확장 유클리드 호제법, 정수론)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-14565%EB%B2%88-%EC%97%AD%EC%9B%90Inverse-%EA%B5%AC%ED%95%98%EA%B8%B0-Java-%ED%99%95%EC%9E%A5-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95-%EC%A0%95%EC%88%98%EB%A1%A0</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-14565%EB%B2%88-%EC%97%AD%EC%9B%90Inverse-%EA%B5%AC%ED%95%98%EA%B8%B0-Java-%ED%99%95%EC%9E%A5-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95-%EC%A0%95%EC%88%98%EB%A1%A0</guid>
            <pubDate>Sat, 26 Jul 2025 05:32:39 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/14565">https://www.acmicpc.net/problem/14565</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/887a92f8-3b04-4ca2-b478-8bbc292091e5/image.png" alt=""></p>
<hr>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b856680ccaf12f65b45805c3f?pvs=21">확장 유클리드 호제법 ⭐⭐⭐</a> </p>
<hr>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li><p>덧셈역을 구하는 것은 어렵지 않다.</p>
<ul>
<li>덧셈역은 (a + b) % n = 0을 만족하는 b의 값을 찾는 것이다.</li>
<li>여기서 a, b의 값은 0 &lt;= a, b &lt; n 을 만족한다.</li>
<li>따라서 덧셈역은 그냥 n - a로 계산하면 된다.<ul>
<li>b = n - a (n &gt; a이므로 n - a &gt; 0일 것이다. 또한 n에서 0혹은 양수 값을 갖는 a값을 빼므로 b는 역시 n보다 작은 값을 가질 수 밖에 없으므로 0 &lt;= b &lt; n값을 갖는 다는 것은 자동으로 만족이 된다.)</li>
</ul>
</li>
</ul>
<hr>
</li>
<li><p>곱셈역</p>
<ul>
<li>곱셈역은 확장 유클리드 호제법을 이용하여 문제를 푼다.</li>
<li>여기서 확장 유클리는 호제법이란 ax+by=gcd(a, b)를 만족하는 정수 해 x, y 값을 찾는 알고리즘이다.</li>
<li>하지만 (a*c)%n=1의 식을 어떻게 ax+by=c와 같은 식으로 해석해야 할지 어려웠다.</li>
</ul>
</li>
</ul>
<blockquote>
<p> <strong>✅ (a * c) % n = 1 해석</strong>
  여기서 (a * c)의 값은 n으로 나누었을 때 나머지 값이 1이 나와야 하므로
  a x c = 1 x n + 1 ⇒ a x c - 1 x n = 1
  a x c = 2 x n + 1 ⇒ a x c - 2 x n = 1
  a x c = 3 x n + 1 ⇒ a x c - 3 x n = 1
  a x c = 4 x n + 1 ⇒ a x c - 4 x n = 1
  … 
  이런식들이 나온다.  근데 문제에서는 a, n의 값을 알고 있다. 
  예시 입력과 같이 n = 26, a = 11이라고 주어져 있다면
  11c - 26d = 1을 만족시키는 정수해 c, d를 구하는 것 같다.
  여기서 나오는 해 c가 곱셈역의 값이 된다. (단, c는 0~n-1사이의 정수여야 한다.)
  (여기서 d는 위의 식에서 1xn, 2xn, 3xn에서 변화하는 값이 1, 2, 3을 나타낸 것이다.)</p>
</blockquote>
<ul>
<li><p>그렇다면 예시입력 1: 26x’ - 11y’ = 1을 확장 유클리드 호제법을 통해 곱셈역을 구해보자</p>
<pre><code> ![](https://velog.velcdn.com/images/jina_ham/post/2fc2e453-8a59-41f3-825c-94d3a1199d6c/image.png)</code></pre><p>⇒ 여기서 x값은 -7로 나오지만 곱셈역 c는 0~25 사이의 값을 가져야 한다. </p>
</li>
</ul>
<blockquote>
<p>** ✔️모듈러 연산**  
이럴 땐 s값 (-7) 에 26 씩 더해가면서 양수가 되게 만들면 된다.
        실제 식으로 보면 다음과 같습니다.
        11 * (-7 + 26 - 26) + 26 * 3 = 1 로 바꿀 수 있고,
        11 * (19 - 26) + 26 * 3 = 1
        11 * 19 + 26 * (-11 + 3) = 1
        11 * 19 + 26 * (-8) = 1 로 변형이 가능하기 때문이다.</p>
</blockquote>
<p> ※ 모듈러 연산은 결과 값에 N을 아무리 많이 더해도 결국 같은 값을 얻는다 연산이다. </p>
<ul>
<li>따라서 예시 입력1의 최종 곱셈역은 -7에 26을 더한 -7 + 26 = 19이다.</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

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());
        long N = Long.parseLong(st.nextToken());
        long A = Long.parseLong(st.nextToken());

        long addInverse = (N - A);

        if(gcd(A, N) != 1) System.out.println(addInverse + &quot; &quot; + -1);
        else {
            long result = multiplyInverse(A, N)[0];
            while(result &lt; 0) { // 해가 음수가 나오면 양수로 보정하기 
                result += N;
            }
            System.out.println(addInverse + &quot; &quot; + result);
        }
    }

    // 곲셈역 구하기 (확장 유클리드 호제법 사용)
    private static long[] multiplyInverse(long A, long N) {
        long[] ret = new long[2]; // ret은 정수 해 x, y를 저장하는 배열
        if (N == 0) {
            ret[0] = 1;
            ret[1] = 0; // x = 1, y = 0
            return ret;
        }
        long q = A / N; // 몫
        long[] v = multiplyInverse(N, A % N);  // 재귀적으로 확장 유클리드, v는 이전 x&#39;, y&#39;값을 저장하는 배열
        ret[0] = v[1]; // x = y&#39;
        ret[1] = v[0] - v[1] * q; // y = x&#39; - y&#39; * q
        return ret;
    }

    private static long gcd(long a, long b) {
        if(b == 0) return a;
        else return gcd(b, a % b);
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>곱셈역을 구하는 과정을 확장 유클리드 호제법과 연관시켜 문제를 해결하는 것이 어려웠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1735번: 분수 합 (Java, 유클리드 호제법, 정수론)]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1735%EB%B2%88-%EB%B6%84%EC%88%98-%ED%95%A9-Java-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95-%EC%A0%95%EC%88%98%EB%A1%A0-wzgqgvh1</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-1735%EB%B2%88-%EB%B6%84%EC%88%98-%ED%95%A9-Java-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95-%EC%A0%95%EC%88%98%EB%A1%A0-wzgqgvh1</guid>
            <pubDate>Fri, 25 Jul 2025 04:42:05 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/1735">https://www.acmicpc.net/problem/1735</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/d809087a-3133-4330-b84c-dc4e2f53e4b0/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념">✔️관련 알고리즘 개념</h2>
<p><a href="https://www.notion.so/228f769b85668134bc6cce23789e4417?pvs=21">유클리드 호제법</a> </p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<ul>
<li>예시 입력이 2/7 + 3/5이면 두 분수를 7과 5의 곱으로 통분한다.<ul>
<li>해당 예시 입력은 35로 통분해준다.</li>
<li>10/35 + 21/35 = 31/35이고 계산 결과가 기약분수가 아니라면</li>
<li>분모, 분자의 최대 공약수로 나눠준다.<ul>
<li>최대 공약수를 구할 때는 유클리드 호제법에 따라 gcd함수를 구현하여 구한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

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 a = Integer.parseInt(st.nextToken());
        int b = Integer.parseInt(st.nextToken());

        st = new StringTokenizer(br.readLine());
        int c = Integer.parseInt(st.nextToken());
        int d = Integer.parseInt(st.nextToken());

        // 통분해서 분수 계산하기
        a *= d;
        c *= b;

        int numerator = a + c;
        int denominator = b * d;

        int gcd = gcd(numerator, denominator);
        System.out.println(numerator / gcd + &quot; &quot; + denominator / gcd);
    }

    static int gcd(int a, int b) {
        if(b == 0) return a;
        else return gcd(b, a%b);
    }
}
</code></pre>
<h2 id="☑️-채점-결과---맞음">☑️ 채점 결과 :  맞음</h2>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>없음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 19577번: 수학은 재밌어 (Java, 오일러피, 정수론) ]]></title>
            <link>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-19577%EB%B2%88-%EC%88%98%ED%95%99%EC%9D%80-%EC%9E%AC%EB%B0%8C%EC%96%B4-Java-%EC%98%A4%EC%9D%BC%EB%9F%AC%ED%94%BC-%EC%A0%95%EC%88%98%EB%A1%A0</link>
            <guid>https://velog.io/@jina_ham/%EB%B0%B1%EC%A4%80-19577%EB%B2%88-%EC%88%98%ED%95%99%EC%9D%80-%EC%9E%AC%EB%B0%8C%EC%96%B4-Java-%EC%98%A4%EC%9D%BC%EB%9F%AC%ED%94%BC-%EC%A0%95%EC%88%98%EB%A1%A0</guid>
            <pubDate>Thu, 24 Jul 2025 09:18:04 GMT</pubDate>
            <description><![CDATA[<h2 id="☑️-문제">☑️ 문제</h2>
<p><a href="https://www.acmicpc.net/problem/19577">https://www.acmicpc.net/problem/19577</a>
<img src="https://velog.velcdn.com/images/jina_ham/post/972da9cc-0913-41d6-9e5e-96c2c6967cb6/image.png" alt=""></p>
<h2 id="✔️관련-알고리즘-개념---오일러피-함수">✔️관련 알고리즘 개념 - 오일러피 함수</h2>
<p><a href="https://www.notion.so/228f769b856681ddbb0ef8ffaedf8ac9?pvs=21">오일러피</a></p>
<h2 id="☑️-문제-분석">☑️ 문제 분석</h2>
<blockquote>
<p>어떤 양의 정수 <em>n</em>이 있다고 할 때, <em>xφ</em>(<em>x</em>) = <em>n</em>을 만족하는 양의 정수 <em>x</em>가 존재하는가?</p>
</blockquote>
<p>양의 정수 <em>x</em>가 <strong><em>존재하면</em></strong> ⇒ 최소의 <em>x</em>를,  <strong><em>존재하지 않으면</em></strong> ⇒ −1을 출력한다.</p>
<hr>
<p>위 조건을 만족하도록 알고리즘을 구성해야 한다.</p>
<p>⇒ 여기서 x값은 n보다 작거나 같은 양의 정수에서 찾으면 된다.</p>
<p>⇒ 만약 x가 n보다 큰 양의 정수라면 x∅[x] &gt; n이 될 것이기 때문에 문제의 조건을 만족할 수가 없기 때문이다. </p>
<ul>
<li><p>예시 입력 1과 같이 n = 2라고 주어진다면</p>
  <img src="https://velog.velcdn.com/images/jina_ham/post/bfbdbdb4-931c-4407-ab6a-2cc5f361e46d/image.png" width="200"/>

<p>  x∅[x] = n을 만족하는 최소 x값은 2이다.</p>
<p>  2 x ∅[2] = 2 * 1 = 2값으로 2와 같으므로 양의 정수 x가 존재하고 그 최소값이 2이므로 2를 출력한다.</p>
</li>
<li><p>예시 입력 2와 같이 n = 3이라고 주어진다면</p>
<img src="https://velog.velcdn.com/images/jina_ham/post/40d8662b-2e35-435d-b948-bb2ef6d53096/image.png" width="200" /></li>
<li><p>x∅[x] = n을 만족하는 양의 정수 x가 존재하는지 확인해야 한다.</p>
<ul>
<li>x = 2일 때, 2 * ∅[2] ≠ 3</li>
<li>x = 3일 때, 3 * ∅[3] ≠ 3 이므로 양의 정수 x가 존재하지 않으므로 -1을 출력한다.</li>
</ul>
</li>
<li><p>예시 입력 3과 같이 n = 20이라고 주어진다면</p>
<p>  <img src="https://velog.velcdn.com/images/jina_ham/post/9250fc3b-e5ea-40c2-9066-f09d81c87fe5/image.png" alt=""></p>
</li>
</ul>
<pre><code>- x를 2부터 반복했을 때, x = 2, 3, 4일 때는 조건을 만족하지 않는다.
- x = 5일 때, 5 * ∅[5] = 5 * 4 = 20 == 20이므로 이를 만족하는 최소 양의 정수값은 5이므로 5를 출력하고 반복문을 멈춘다.</code></pre><p>위의 예시 입력을 토대로 문제 풀이 순서롤 써보면</p>
<ol>
<li>n을 입력받은 다음 ∅[n]함수를 정의한다.</li>
<li>1~n까지의 값을 갖는 ∅[n]함수를 정의한다.</li>
<li>∅[n] 배열에서 인덱스를 앞에서 부터 반복하면서 x∅[x] == n를 만족하는 양의 정수 n이 존재하는지 찾는다.</li>
</ol>
<p>⇒ 이 방법은 메모리 초과가 발생한다.</p>
<hr>
<p>그래서 문제 풀이를 수정해보았다.</p>
<ol>
<li>n을 입력받은 다음 n보다 작거나 같은수를 2부터 차례대로 반복하며</li>
<li>문제 조건을 만족하는 x값을 찾는다. (위의 방법과 다른 점은 x에 대한 오일러 피 함수를 따로 정의하여 전체 오일러피 배열을 정의하는 것이 아니라 하나의 x값에 대한 오일러피 값을 그때 그때 계산하는 것이다.)</li>
<li>만족하는 x값을 찾으면 결과 출력</li>
</ol>
<p>⇒ 하지만 이 방식은 시간초과가 발생한다. 탐색시 불필요한 탐색을 제거하는 방향으로 가야 한다. </p>
<h3 id="⚠️여기가-이-문제-해결의-핵심이다">⚠️여기가 이 문제 해결의 핵심이다!!!</h3>
<blockquote>
<p>우리는 문제를 해결하기 위해 n보다 작은 모든 수에 대해 반복할 필요는 없다. x는 정수고, φ(x)도 정수고, n도 정수이다. 식을 φ(x) = n/x로 변형해서 생각해보면, φ(x)가 정수가 나오려면 n/x역시 정수가 나와야 한다. n/x가 정수가 나오려면 x가 n의 약수인 경우밖에 없다.  <strong>바꿔 말해서 이제 이 문제는 n의 약수인 x에 대해 φ(x)를 구하는 문제로 바뀌게 됩니다.</strong> ⇒ n에 대한 약수 배열을 구한 후 해당 배열을 순회하면서 문제를 푼다.</p>
</blockquote>
<hr>
<h2 id="☑️-코드">☑️ 코드</h2>
<pre><code class="language-jsx">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class Main {
    static int n;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine()); // 자연수

        int result = -1;

        // 약수만 순회하도록 개선
        for (int x : getDivisors(n)) {
            if (x * phi(x) == n) {
                result = x;
                break;
            }
        }

        System.out.println(result);
    }

    // 약수 리스트를 반환하는 함수
    private static ArrayList&lt;Integer&gt; getDivisors(int num) {
        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;(); // num의 약수를 저장하는 배열 초기화
        for (int i = 1; i * i &lt;= num; i++) {
            if (num % i == 0) {
                list.add(i);
                if (i != num / i) list.add(num / i);
            }
        }
        list.sort(Integer::compareTo); // 작은 수부터 탐색하게 정렬
        return list;
    }

    // 오일러 피 함수
    private static int phi(int x) {
        int value = x;
        int temp = x;

        for (int i = 2; i * i &lt;= temp; i++) {
            if (temp % i == 0) {
                value = value - value / i;
                while (temp % i == 0) temp /= i;
            }
        }
        if (temp &gt; 1) {
            value = value - value / temp;
        }
        return value;
    }
}
</code></pre>
<h2 id="☑️-채점-결과---메모리-초과-→-시간-초과-→-맞음">☑️ 채점 결과 :  메모리 초과 → 시간 초과 → 맞음</h2>
<p>(n의 소수 배열을 별도로 생성하여 답을 구하는 과정은 gpt도움을 받음)
<img src="https://velog.velcdn.com/images/jina_ham/post/300e2d22-8bcb-4821-8f2e-34b790d7d8ac/image.png" alt=""></p>
<h2 id="☑️-어려웠던-점">☑️ 어려웠던 점</h2>
<ul>
<li>탐색 조건을 추가하여 시간 초과 문제를 해결하는 것이 어려움</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>