<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>chanBaB_.log</title>
        <link>https://velog.io/</link>
        <description>찬밥신세</description>
        <lastBuildDate>Thu, 03 Jul 2025 03:01:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. chanBaB_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ye_chan_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[스레드 라이브러리]]></title>
            <link>https://velog.io/@ye_chan_/%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@ye_chan_/%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Thu, 03 Jul 2025 03:01:42 GMT</pubDate>
            <description><![CDATA[<p>스레드 라이브러리는 스레드를 생성 및 관리를 위한 API를 제공해준다. 
스레드 라이브러리는 커널의 지원 없이 <strong>완전한 사용자 공간에 제공하는 방법</strong>과 운영체제에 의해 <strong>커널 수준 라이브러리를 구현</strong>하는 경우가 있다.</p>
<p>첫 번째 방법(사용자 공간)의 라이브러리 함수 호출은 <strong>사용자 공간의 지역함수를 호출</strong>하게 되는 것을 의미하고,
두 번째 방법(커널 수준 라이브러리) 함수 호출은 <strong>커널 시스템 콜</strong>을 부르는 결과를 낳는다.</p>
<p>현재 주로 사용되는 라이브로리는 POSIX Pthreads, Windows, Java가 있다.</p>
<p>POSIX Pthreads는 사용자 또는 커널 수준 라이브러리로 제공될 수 있다.
Windows 스레드는 윈도우 시스템에서 사용 가능한 커널 수준 라이브러리이다.
Java 스레드는 자바 프로그램에서 직접 생성과 관리를 가능케 한다. JVM은 운영체제에서 실행되기 때문에 Java 스레드는 호스트 시스템에서 사용 가능한 스레드 라이브러리로 구현된다.</p>
<ul>
<li>windows - windows API</li>
<li>UNIX,Linux,macOS - Pthreads</li>
</ul>
<h3 id="pthreads">Pthreads</h3>
<p>Pthreads는 POSIX가 스레드 생성 및 동기화를 위해 저장한 표준 API이다. 즉, 명세일 뿐, 구현은 운영체제 설계자가 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[다중 스레드 모델]]></title>
            <link>https://velog.io/@ye_chan_/%EB%8B%A4%EC%A4%91-%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@ye_chan_/%EB%8B%A4%EC%A4%91-%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Thu, 03 Jul 2025 02:51:08 GMT</pubDate>
            <description><![CDATA[<p>앞서 배운 스레드는 일반적인 의미의 스레드다. 그러나 스레드는 사용자 수준의 <strong>사용자 스레드(user threads)</strong>와 커널 수준의 <strong>커널 스레드(kernel threads)</strong>로 구분된다.</p>
<p>사용자 스레드는 커널 위에서 지원되며, 커널의 지원 없이 사용자 공간에서 관리된다.
ex)POSIX Pthreads, Windows threads, Javathreads</p>
<p>커널 스레드는 운영체제가 직접 지원하고 관리한다.</p>
<p>즉, 사용자 스레드는 사용자가 자유롭게 생성할 수 있지만, 실제로 CPU에서 실행되기 위해서는 커널 스레드를 통해야 한다.</p>
<h3 id="다대일-모델many-to-one">다대일 모델(Many-to-One)</h3>
<p>많은 사용자 수준 스레드를 하나의 커널 스레드로 사상한다.
문제는 한 스레드가 봉쇄형 시스템 콜을 할 경우, 전체 프로세스가 봉쇄된다. 
또한 한 번에 하나의 스레드만 커널에 접근 가능해 병렬 실행이 불가능하다.</p>
<h3 id="일대일-모델one-to-one">일대일 모델(One-to-One)</h3>
<p>사용자 스레드와 커널 스레드가 1대1 매칭이 된다.
하나의 쓰레드가 봉쇄 되어도 다른 쓰레드는 실행된다.
병렬적으로 멀티프로세싱이 가능하다.
다만 너무 많은 쓰레드를 올리면 시스템 자원이 고갈될 수 있다.</p>
<h3 id="다대다-모델many-to-many">다대다 모델(Many-to-Many)</h3>
<p>여러개의 사용자 모델과 작거나 같은 수의 커널 스레드로 멀티플렉스한다.
죽, 필요한 만큼 사용자 스레드를 생성하고 그에 상응하는 커널 스레드를 통해 병렬로 수행할 수 있다,</p>
<p>그러나 1.구현이 너무 어렵고 2.요새는 워낙 코어가 많아서 굳이 사용하지 않는다.</p>
<h3 id="두-수준-모델two-level-model">두 수준 모델(two-level model)</h3>
<p>다대다 모델과 일대일 모델을 섞었다. 그러나 구현이 어려워 사용하지 않는다.</p>
<h3 id="결론">결론</h3>
<p>현대 운영체제는 일대일 모델을 사용한다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스레드란?]]></title>
            <link>https://velog.io/@ye_chan_/%EC%8A%A4%EB%A0%88%EB%93%9C%EB%9E%80</link>
            <guid>https://velog.io/@ye_chan_/%EC%8A%A4%EB%A0%88%EB%93%9C%EB%9E%80</guid>
            <pubDate>Thu, 03 Jul 2025 02:13:37 GMT</pubDate>
            <description><![CDATA[<h3 id="스레드란">스레드란?</h3>
<p>스레드는 CPU이용의 기본 단위이다.전통적인 프로세스는 하나의 스레드만 가지지만, 만일 프로세스가 다수의 스레드를 갖게 된다면 동시에 하나 이상의 작업을 수행할 수 있다. 또한 같은 프로세스 내의 여러 스레드는 서로 자원들을 공유한다.
<img src="https://velog.velcdn.com/images/ye_chan_/post/f2bb0888-3d10-40e8-b22f-b15d7610bc37/image.png" width="30%" height="30%"></p>
<blockquote>
<p>스레드의 구성은 어떻게 될까?</p>
</blockquote>
<ul>
<li>스레드ID</li>
<li>프로그램 카운터(PC)</li>
<li>레지스터 집합</li>
<li>스택</li>
</ul>
<h3 id="스레드가-필요한-이유는">스레드가 필요한 이유는?</h3>
<p>프로세스의 생성 작업은 매우 많은 시간을 소요하고 많은 자원을 필요로 한다. 그런데 새 프로세스의 작업이 기존 프로세스의 작업과 동일하다면..? 새로 만드는 것 보다 같은 프로세스안에 여러 스레드를 만드는 것이 효율적이다.
스레드는 프로세스의 자원을 공유하기 때문에!</p>
<h4 id="스레드-사용-예시">스레드 사용 예시</h4>
<p>웹 서버는 여러개의 클라이언트로부터 요청을 받으면, 그 요청을 수행한 별도의 프로세스를 만든다.
<img src="https://velog.velcdn.com/images/ye_chan_/post/b343187c-e6cc-4b5e-b90c-9c0d3096bbd3/image.png" alt=""></p>
<h3 id="장점">장점</h3>
<p>이러한 스레드 프로그래밍은 어떤 이점이 있을까?</p>
<ol>
<li><strong>응답성</strong>
응용 프로그램이 긴 작업을 수행하더라도, 연산이 오래 걸리는 작업을 <strong>별도의 스레드에서 실행</strong>된다면 프로그램이 계속 수행되는 것을 허용함으로써 사용자의 응답성을 증가시킨다.</li>
<li>** 자원 공유**
프로세스들은 공유 메모리와 메시지 전달 기법을 통해 자원을 공유한다. 그러나 스레드는 그들이 속한 <strong>프로세스의 자원들과 메모리를 공유</strong>한다.</li>
<li>** 경제성**
자원 공유를 통해 스레드를 생성 및 문맥 교환시 프로세스를 생성하거나 문맥 교환하는 것보다 경제적이다. </li>
<li><strong>규모 적응성</strong>
다중 스레드는 다중 처리기 구조에서 각각의 스레드가 <strong>병렬로 수행</strong>될 수 있으므로 적응성이 높다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[java 스레드 구현 - 생성하기]]></title>
            <link>https://velog.io/@ye_chan_/%EC%9E%90%EB%B0%94thread</link>
            <guid>https://velog.io/@ye_chan_/%EC%9E%90%EB%B0%94thread</guid>
            <pubDate>Thu, 26 Jun 2025 03:43:15 GMT</pubDate>
            <description><![CDATA[<p>스레드를 자바에선 JVM에 의해 관리 된다. 프로세스는 적어도 하나의 쓰레드를 가지고 있는데, 자바는 main쓰레드를 필수로 갖게 된다.</p>
<h2 id="구현-방법">구현 방법</h2>
<p>자바에서 스레드를 생성하는 방법은 1.스레드를 상속받거나 2.Runnable인터페이스를 구현하는 방법이 있다.</p>
<p><strong>1. 스레드 상속</strong>
스레드의 실행은 run()메서드를 통해 실행된다. run() 함수를 오버라이드 하면 된다.</p>
<pre><code class="language-java">class ThreadEx1_1 extends Thread {
    public void run() {
        for (int i=0; i&lt;5; i++){
            System.out.println(getName());
        }
    }
}</code></pre>
<p><strong>2.runnable interface</strong>
인터페이스를 구현하여 쓰레드 객체를 생성하면 된다. 마찬가질 run()메서드를 구현해야 한다.</p>
<pre><code class="language-java">class ThreadEx1_2 implements Runnable {
    @Override
    public void run() {
        for (int i=0; i&lt;5; i++){
            System.out.println(Thread.currentThread().getName());
            public static native Thread currentThread(); 
            //Thread의 static 메소드로 자신의 스레드를 참조함 &lt;- 자신의 스레드의 getName()을 부르는 거임

        }
    }
}</code></pre>
<p>아래는 runnable interface를 통한 스레드 객체 생성 방식이다.</p>
<pre><code class="language-java">public Thread(Runnable task) {
    this(null, null, 0, task, 0, null);
} // &lt;- Thread 생성자: Runnable 객체를 받아서 Thread 객체를 생성함

public void run() {
    Runnable task = holder.task;  // Thread에 저장된 Runnable 객체 가져오기
    if (task != null) {
        Object bindings = scopedValueBindings();  // 스코프 바인딩 정보 가져오기
        runWith(bindings, task);  // 실제 Runnable 실행
    } // &lt;- Thread의 run() 메서드: 저장된 Runnable의 run() 메서드를 실행함
}</code></pre>
<p>스레드는 thread.start()를 통해 실행되고,실행되면 run()을 통해 진행한다. thread.run()을 진행하면 새로운 스택을 실행하는 것이 아닌 main쓰레드의 스택을 사용하므로 주의해야 한다.</p>
<pre><code class="language-java">    public static void main(String[] args) {

        ThreadEx1_1 t1 = new ThreadEx1_1(); // thread 자손 클래스의 인스턴스 생성
        Runnable r = new ThreadEx1_2(); // runnable을 구현한 클래스의 인스턴스 생성
        Thread t2 = new Thread(r);
        t1.start();//스레드를 생성하고 사용될 호출스택 생성 -&gt; run() 만들어진 호출스택에 생성
        t2.start();
        t1 = new ThreadEx1_1();
        t1.start(); //스레드 다시 실행시 예외 발생
    }
}</code></pre>
<blockquote>
<p>thread.start()와 thread.run() 차이</p>
</blockquote>
<pre><code>- thread.run()
Main Thread Call Stack
┌────────────────────────────┐
│ main()                     │
│ └─ thread.run() 호출        │
│     └─ run() 메서드 실행      │
└────────────────────────────┘
- thread.start()
 Main Thread Call Stack          New 
Thread Call Stack
┌────────────────────────┐     ┌─────────────────────┐
│ main()                 │     │ run()               │
│ └─ thread.start() 호출  │     └─────────────────────┘
└────────────────────────┘     </code></pre><h4 id="getname-과threadcurrentthreadgetname의-차이">getName() 과Thread.currentThread().getName()의 차이</h4>
<p>Thread 객체의 이름을 가져오는 메서드는 getName()이다. 
하지만 Runnable 인터페이스를 구현한 객체는 Thread가 아니기 때문에 getName() 메서드를 직접 호출할 수 없다.
따라서 현재 실행 중인 스레드의 이름을 얻고 싶다면, Thread.currentThread()를 호출해 현재 스레드 객체를 얻은 뒤, getName()을 호출해야 한다.</p>
<blockquote>
<p>this 가 가르키는 객체</p>
</blockquote>
<ul>
<li>thread - thread</li>
<li>runnable - runnable 구현 객체</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 8980 번] 택배]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-8980-%EB%B2%88-%ED%83%9D%EB%B0%B0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-8980-%EB%B2%88-%ED%83%9D%EB%B0%B0</guid>
            <pubDate>Sat, 30 Nov 2024 10:53:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/8980">https://www.acmicpc.net/problem/8980</a></p>
</blockquote>
<p>학교 알고리즘 시간에 그리디를 배우고 있어서 문제를 풀어봤다.
분할 가능한 배낭문제 인것 같다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li>도착 시간이 빠른 순서대로 정렬한다.</li>
<li>같을 경우, 출발 시간은 같은 순서대로 정렬한다.</li>
<li>출발~도착까지의 적재 가능한 양보다 적으면 박스의 무게만큼 더한다.
 3-1. 만약 적재 가능한 양보다 크다면 가능한 만큼만 쪼개서 더한다.</li>
<li>반복한다.</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-java">import java.util.*;

class boxes implements Comparable&lt;boxes&gt; {
    int start;
    int end;
    int weight;

    public boxes(int s, int e, int w) {
        start = s;
        end = e;
        weight = w;
    }

    @Override
    public int compareTo(boxes o) {
        if (this.end == o.end) {
            return this.start - o.start;
        }
        return this.end - o.end;
    }
}

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

        boxes[] boxes = new boxes[m];
        for (int i = 0; i &lt; m; i++) {
            boxes[i] = new boxes(sc.nextInt(), sc.nextInt(), sc.nextInt());
        }

        Arrays.sort(boxes);

        int[] village = new int[n + 1];
        int cnt = 0;

        for (boxes box : boxes) {
            int maxC = c;

            for (int i = box.start; i &lt; box.end; i++) {
                maxC = Math.min(maxC, c - village[i]);
            }

            int lowWeight = Math.min(box.weight, maxC); //만약 적재량 &lt; 박스 무게라면 박스를 쪼갬.

            for (int i = box.start; i &lt; box.end; i++) {
                village[i] += lowWeight;
            }

            cnt += lowWeight;
        }

        System.out.println(cnt);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 11049] 행렬 곱셈 순서]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-11049-%ED%96%89%EB%A0%AC-%EA%B3%B1%EC%85%88-%EC%88%9C%EC%84%9C</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-11049-%ED%96%89%EB%A0%AC-%EA%B3%B1%EC%85%88-%EC%88%9C%EC%84%9C</guid>
            <pubDate>Tue, 29 Oct 2024 02:45:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/11049">https://www.acmicpc.net/problem/11049</a></p>
</blockquote>
<p>험난했던 시험 기간을 끝내고 다시 돌아온 백준 시간. 오늘 푼 문제는 알고리즘 시간에 배운 행렬곱셈 순서를 실제로 풀어본 문제이다. 현재 학교 알고리즘 과목에서 DP를 배우고 있다.</p>
<h3 id="풀이-방법">풀이 방법</h3>
<p>우선 행렬의 곱셈 성질에 대해서 알아야 한다.
행렬 <code>A(p*q)</code> 와<code>B(q*r)</code>를 곱하면 총 연산은<code>p*q*r</code>이고, AB크기는<code>p*r</code>이 된다.
<img src="https://velog.velcdn.com/images/ye_chan_/post/567e12fe-58a7-4a01-9653-6d4746062aab/image.jpeg" width="500" /></p>
<p>이를 인지했으니, 본격적으로 시작해보자.
$A_1$부터 $A_n$까지의 행렬이 존재하였을 때, $A_n$이 가능한 곱셈의 연산 횟수는 n-1이다.
$(A_1 * A_2 .. A_{n-1}) * A_n$
$(A_1 * A_2 .. A_{n-2})(A_{n-1} * A_n)$ 등등..
<img src="https://velog.velcdn.com/images/ye_chan_/post/2a707b6a-6140-4e3b-9f3d-59bb2c2b307f/image.jpeg" width="300" />
이 중 가장 연산 횟수가 적은 값을 찾으면 된다는 것이다.
그렇다면 $(A_1 * A_2 .. A_{n-1})$의 값은? 마찬가지로 n-2의 경우의 수 중에서 가장 작을 값을 ... 이렇게 계속 내려가 재귀적으로 구할 수 있다.
그러나, 정말 재귀적으로 풀기에는 경우의 수도 많고, <strong>중복된 내용</strong>도 많다.
또한 큰 문제를 해결하기 위해, 작은 부분을 해결하는 <strong>최적 부분 구조</strong>도 가지고있다. 그러므로 <strong>DP를 사용해서 풀어야 한다.</strong></p>
<h4 id="최적부분-구조">최적부분 구조</h4>
<p>$A_i$ ~ $A_j$까지의 최소 연산을 $C_{i~j}$라고 한다면, </p>
<blockquote>
<p>$C_{i<del>j}$는 임 의의 K(i&lt;=k&lt;=j-1)을 기준으로 **$C_{i</del>k}$ + $C_{k+1~j}$ + 두 행렬의 곱셈 연산 수**이다.</p>
</blockquote>
<p>예) $C_{1<del>4}$를 구할 때, $C_{1</del>2}$와 $C_{3~4}$를 가지고 구함 (1=A 2=B 3=C 4=D)</p>
<img src="https://velog.velcdn.com/images/ye_chan_/post/1ee7fb90-e230-421c-b263-65fbebdb94df/image.jpeg" width="500">

<p>즉,
<img src="https://velog.velcdn.com/images/ye_chan_/post/64599a73-eb22-428e-88b4-bdf0f4b3c1fd/image.jpeg" width="450">
이 된다. 이제 연산을 구해보자.
우선, 행렬의 곱의 특징은, 곱하려는 AB의 행과 열이 같아야 한다.
즉, 겹치는 부분이 존재한다. $A_1$~$A_4$까지의 행렬이 있으면 <code>p*q q*r r*x x*z</code>
이렇게 행렬이 겹친다. 중복된 부분을 제거해보자.
<img src="https://velog.velcdn.com/images/ye_chan_/post/758e4b26-36f7-4fad-ae0a-8df1bddd378b/image.jpeg" width="400"></p>
<p>$A_i$의 행은 i-1, 열은 i가 됨을 알 수 있다.이를 연산에 집어 넣어보면 $P_{i-1}P_{K}P_{j}$가 된다.(만약 이부분이 이해가 안된다면 행렬을 곱했을 때, 연산 후 행렬의 크기가 p*r임을 잊지 말자)</p>
<p>후, 이제 최적부분 구조도 알았겠다,이제 아래서부터 차근차근 올라가보자
그룹의 체인(괄호로 묶는 묶음)은 자기 자신<del>n개까지 즉, 1개,2개..n개만큼 묶을 수 있다.
나 자신만을 묶는다면 $C_{i</del>i}$가 될 것이고, 2개를 묶는다면 $C_{i<del>i+1}$이 될 것이다.결국i</del>j까지의 원소가 모두 묶일때까지 반복한다. 
<img src="https://velog.velcdn.com/images/ye_chan_/post/af57e430-5d60-4feb-ad72-df0e98d2cb10/image.jpeg" alt=""></p>
<p><strong>시간복잡도</strong>
체인의 길이n * 행렬의 개수n * </p>
<h3 id="코드">코드</h3>
<pre><code class="language-java">
import java.util.Scanner;

public class Main {


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] p= new int[n+1];
        for(int i=1;i&lt;=n;i++){
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            p[i-1]=a;
            p[i]=b;
        }
        int[][] m = new int[n+1][n+1];
        for(int i=1;i&lt;=n;i++){
            m[i][i] = 0;
        }
        for(int r=2;r&lt;=n;r++){
            for(int i=1;i&lt;=n-r+1;i++){
                int j =i+r-1;
                int minValue = Integer.MAX_VALUE;
                for(int k=i;k&lt;=j-1;k++){
                    minValue = Math.min(minValue,m[i][k]+m[k+1][j]+(p[i-1]*p[k]*p[j]));
                }
                m[i][j] = minValue;
            }
        }
        System.out.println(m[1][n]);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 3151] 합이 0]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-3151-%ED%95%A9%EC%9D%B4-0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-3151-%ED%95%A9%EC%9D%B4-0</guid>
            <pubDate>Mon, 23 Sep 2024 12:25:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/3151">https://www.acmicpc.net/problem/3151</a></p>
</blockquote>
<p>값이 중복되지 않게 예외 처리를 해줘야 하는데 이 점을 내가 간과해서 꽤나 애를 먹었다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li>세 수를 합해야 하므로 모든 수를 따지면 $N^3$이므로 시간 초과이다.</li>
<li>그러나 두 수를 더하고 0에 수렴하는 나머지 수를 찾는다면 $N^2logN$이 된다. n&lt;10000이므로 $N^2$은 1억이라 4초안엔 어지간하면 해결될 것 같다.</li>
<li>그래서 나는 두 수를 찾는 이중 포문과 두 값과 더했을 때 0이 되는 값을 이진 탐색으로 찾았다.</li>
<li>다만 같은 값이 여러개 있어도 모두 독립된 값이므로 이를 예외처리 하기 위해 upper_bound - lower_bound 하여 개수를 더하였다.</li>
<li>또한 0번배열부터 순차적으로 조합해 나가므로 m번 배열에선 m-1번 배열까지는 쳐다 볼 필요도 없다. 이를 예외처리 해줘야 한다.</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;algorithm&gt;
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL); cout.tie(NULL);
    int n;
    cin&gt;&gt;n;
    int arr[10000];
    for(int i=0; i&lt;n; i++){
        cin&gt;&gt;arr[i];
    }
    sort(arr,arr+n);
    long long cnt=0;
    for(int i=0; i&lt;n-2; i++){
        for(int j=i+1; j&lt;n-1; j++){
            int tmp = arr[i] + arr[j];
            int lower = lower_bound(arr +(j+1), arr + n, -tmp) - arr;
            int upper = upper_bound(arr+(j+1), arr + n, -tmp) - arr;
            cnt +=  upper - lower ; 
        }
    }
    cout&lt;&lt;cnt;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[json-server-auth 로그인 구현2]]></title>
            <link>https://velog.io/@ye_chan_/json-server-auth-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%842</link>
            <guid>https://velog.io/@ye_chan_/json-server-auth-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%842</guid>
            <pubDate>Tue, 10 Sep 2024 02:11:00 GMT</pubDate>
            <description><![CDATA[<p>이번엔 실질적으로 권한이 있는 유저만 해당 내용을 볼 수 있게끔 해볼 것이다.
<a href="https://minhanpark.github.io/%EC%8B%9C%EB%A6%AC%EC%A6%88/json-server-auth/">이블로그를</a> 참고해서 만들었다.</p>
<h2 id="1-유저가-로그인하면-todo를-불러오자">1. 유저가 로그인하면 todo를 불러오자.</h2>
<p>먼저 클라이언트에 todo가 보일 수 있게 todo를 저장하는 state를 만들자.</p>
<pre><code class="language-js">const [todoList, setTodoList] = useState([]);</code></pre>
<p>이제 todo를 서버에서 읽어와야한다.
todo에 userId를 저장한다고 가정하고 restAPI를 구현해보자</p>
<pre><code class="language-js">  const readList = async () =&gt; {
    if (!token) {
      console.log(&quot;No token available, cannot read list&quot;);
      return;
    }

    try {
      const { data } = await axios.get(`http://localhost:4444/todos?userId=${userId}`, {
        headers: { Authorization: `Bearer ${token}` }
      });
      setTodoList(data);
      console.log(&quot;Todo list loaded:&quot;, data);
    } catch (error) {
      console.error(&quot;할 일 목록 읽기 실패:&quot;, error.response?.data || error.message);
    }
  };</code></pre>
<p>그러면 언제 todo를 읽어와야 할까?
1.todo가 삭제될 때
2.todo가 생성될 때
3.로그인 할 때</p>
<p>1,2번은 post도 구현해야하니 3번부터 구현하자.
로그인하면 token이 저장되니 token값이 바뀌면 readList 함수를 불러오게끔 해보자</p>
<pre><code class="language-js">  useEffect(() =&gt; {
    if (token) {
      readList();
    }
  }, [token]);</code></pre>
<h3 id="2실질적으로-할-일을-추가할-수-있도록-하자">2.실질적으로 할 일을 추가할 수 있도록 하자.</h3>
<p>우선 화면에 보일 수 있도록 jsx를 작성한다.</p>
<pre><code class="language-js">const [description, setDescription] = useState(&quot;&quot;);

{/* Todo Form */}
      {token &amp;&amp; (
        &lt;&gt;
          &lt;h2&gt;추가하기&lt;/h2&gt;
          &lt;input
            placeholder=&quot;할 일&quot;
            type=&quot;text&quot;
            value={description}
            onChange={(e) =&gt; setDescription(e.target.value)}
          /&gt;
          &lt;button onClick={handleSubmit}&gt;추가하기&lt;/button&gt;
          &lt;br /&gt;
        &lt;/&gt;
      )}</code></pre>
<p> <img src="https://velog.velcdn.com/images/ye_chan_/post/bbcac007-7aec-4437-8129-d9c89bc8ef13/image.png" alt="">
이제 버튼을 눌렀을 때 실제로 할 일이 추가되도록 하자.
todo를 만들기 위해선 1.todo의 내용 2.수행여부 3.userId가 필요하다.
이 내용들을 body에 넣어 restAPI를 보내보자.</p>
<pre><code class="language-cpp">  const handleSubmit = async () =&gt; {
    try {
      const { data } = await axios.post(
        &quot;http://localhost:4444/todos&quot;,
        {
          description,
          isCompleted: false,
          userId 
        },
        {
          headers: { Authorization: `Bearer ${token}` }
        }
      );
      alert(data.description + &quot;이 추가되었습니다.&quot;);
      setDescription(&quot;&quot;);
      await readList(); 
    } catch (error) {
      console.error(&quot;할 일 추가 실패:&quot;, error.response?.data || error.message);
    }
  };</code></pre>
<p>  이제 직접 값을 넣어보자!
  <img src="https://velog.velcdn.com/images/ye_chan_/post/e2386f21-efd2-4d6c-8b30-39d50317c857/image.png" alt="">
값이 야무지게 들어온다.</p>
<p>그렇다면 이제 할 일 목록이 보이게끔 해보자!</p>
<pre><code class="language-js">      {/* Todo List */}
      {token &amp;&amp; (
        &lt;div&gt;
          &lt;h2&gt;할 일 목록&lt;/h2&gt;
          &lt;ul&gt;
            {todoList?.map((todo) =&gt; (
              &lt;div key={todo.id}&gt;
                &lt;li&gt;{todo.description}&lt;/li&gt;
                &lt;button
                  onClick={() =&gt; toggleCompleteBtn(todo.id, todo.isCompleted)}
                &gt;
                  {todo.isCompleted ? &quot;완료&quot; : &quot;미완료&quot;}
                &lt;/button&gt;
                &lt;button onClick={() =&gt; deleteTodoBtn(todo.id)}&gt;삭제하기&lt;/button&gt;
              &lt;/div&gt;
            ))}
          &lt;/ul&gt;
        &lt;/div&gt;
      )}</code></pre>
<p><img src="https://velog.velcdn.com/images/ye_chan_/post/c0ff2204-8056-41d6-89e2-b9fc12e6a3d6/image.png" alt=""></p>
<p>이제 수행여부와 삭제버튼을 만들어보자.
수행여부는 그냥 boolean값을 반대로 해주면 된다.</p>
<pre><code class="language-cpp">  const toggleCompleteBtn = async (id, isCompleted) =&gt; {
    try {
      await axios.patch(
        `http://localhost:4444/todos/${id}`,
        {
          isCompleted: !isCompleted,
        },
        {
          headers: { Authorization: `Bearer ${token}` }
        }
      );
      await readList();
    } catch (error) {
      console.error(&quot;할 일 상태 변경 실패:&quot;, error.response?.data || error.message);
    }
  };</code></pre>
<p>삭제는 delete메소드를 사용했다.</p>
<pre><code class="language-js">
  const deleteTodoBtn = async (id) =&gt; {
    try {
      await axios.delete(`http://localhost:4444/todos/${id}`, {
        headers: { Authorization: `Bearer ${token}` }
      });
      await readList();
    } catch (error) {
      console.error(&quot;할 일 삭제 실패:&quot;, error.response?.data || error.message);
    }
  };</code></pre>
<p>완성!
<img src="https://velog.velcdn.com/images/ye_chan_/post/f29d97f7-c12f-4fa3-946d-760366534381/image.png" alt=""></p>
<p>참고
<a href="https://minhanpark.github.io/%EC%8B%9C%EB%A6%AC%EC%A6%88/json-server-auth/">https://minhanpark.github.io/%EC%8B%9C%EB%A6%AC%EC%A6%88/json-server-auth/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 12015번] 가장 긴 증가하는 부분 수열 2]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-12015%EB%B2%88-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-2</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-12015%EB%B2%88-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-2</guid>
            <pubDate>Tue, 10 Sep 2024 01:12:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/12015">https://www.acmicpc.net/problem/12015</a></p>
</blockquote>
<p>이진탐색의 사용은 무궁무진하구나...
처음엔 엥? 아무리봐도 dp문제인데? 했는데 시간복잡도가 O(N^2)이면 못푸는 문제더라...^^ 아무리봐도 모르겠어서 인터넷 보면서 풀었다..</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li><p>결과 배열을 만들고 첫 번째 입력 배열 값을 넣는다.</p>
</li>
<li><p>입력 배열을 돌며 현재 저장된 결과 배열의 마지막 값 보다 큰지 확인한다.</p>
</li>
<li><p>만약 크면 뒤에 삽입, 그렇지 않으면 현재 원소보다 크거나 같은 첫 번째 결과 값과 바꾼다.</p>
<blockquote>
<p>{10,20,30,15,20,25}    가 존재한다고 했을 때
i=0) result = {10}
i=2) result = {10,20,30}
i=3) restlt = {10,<strong>15</strong>,30}</p>
</blockquote>
<p>이유는 <strong>다른 부분배열로 대체</strong>하기 위해서이다.
20을 15로 바꾼다고해서 길이가 달라지진 않는다.
다만 다음에 20을 만나면 30위치에 20이 들어오고, 25를 만나면 길이가 증가하게 된다.
즉, 15부터 reslt의 end까지 언제나 바뀔 <strong>준비</strong>를 하고있는 것이다.증가하는 부분 수열이므로 결국 작은 값부터 순차적으로 바뀌는 것 이니깐..</p>
</li>
<li><p>끝까지 돌고 나서 길이를 출력한다. 
(result 배열 자체는 증가하는 부분 수열이 아닐 수도 있다. 3번의 이유로 바뀔 준비를 하다가 만 배열일 수도 있기 때문이다.그러나 길이는 동일하다.)</p>
</li>
</ol>
<h4 id="시간복잡도">시간복잡도</h4>
<p>3번에서 대체할 수를 찾는 방법으로 이진탐색을 쓰면 logN으로 금방 찾을 수 있다. n번을 탐색하니 
$O(nlogn)$</p>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
using namespace std;

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

    int n;
    cin &gt;&gt; n;
    vector&lt;int&gt; seq(n + 1);
    vector&lt;int&gt; res;

    for(int i = 1; i &lt;= n; i++){
        cin &gt;&gt; seq[i];
    }

    res.push_back(seq[1]);

    for(int i = 2; i &lt;= n; i++){
        if(seq[i] &gt; res.back()) {
            res.push_back(seq[i]);
        } else {
            auto it = lower_bound(res.begin(), res.end(), seq[i]);
            *it = seq[i];
        }
    }

    cout &lt;&lt; res.size() &lt;&lt; endl;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 15732] 도토리 숨기기]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-15732-%EB%8F%84%ED%86%A0%EB%A6%AC-%EC%88%A8%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-15732-%EB%8F%84%ED%86%A0%EB%A6%AC-%EC%88%A8%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Mon, 09 Sep 2024 14:52:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/15732">https://www.acmicpc.net/problem/15732</a></p>
</blockquote>
<p>와!! 이분탐색 문제중에 처음으로 보자마자 슥슥 접근하고 바로 구현해냈다..
사실 규칙이 쉬워서 금방 발견한거긴 하지만..</p>
<h3 id="문제-풀이">문제 풀이</h3>
<p>우선 문제가 요구하는 값은 &#39;마지막 도토리가 들어가는 상자의 번호&#39;이다. 
마지막 도토리라는 것은 D번째 도토리라는 것이다.
도토리 D가 있을때, 1~i번째 상자까지의 도토리의 합이 D가 된다면 마지막 상자가 된다는 것도 알 수 있다. 그리고 마지막 상자는 결국 &#39;최댓값&#39;이다.
와! 앞서 풀었던 <a href="https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1939-%EC%A4%91%EB%9F%89%EC%A0%9C%ED%95%9C">파라메트릭 서치</a> 문제인 것이다.</p>
<p>그렇다면 i번째의 상자까지의 도토리 D개는 어떻게 알 수가 있을까?
&#39;규칙대로&#39;더해보면 되지 않을까?
규칙을 보면 C번마다 반복하여 도토리를 추가하는 것을 알 수 있었다.즉 C로 나누기를 하면 된다. 다만 시작값과 끝 값이 정해져 있으니 이를 유의해서 계산해주면 된다.</p>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

struct rule
{
    int s,e,j;
    rule(int s,int e, int j):s(s),e(e),j(j){};
};


int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL); cout.tie(NULL);
    int result;
    int n, k;
    int d;
    cin&gt;&gt;n&gt;&gt;k&gt;&gt;d;
    int s=0,&amp;e=n;
    vector &lt;rule&gt; rule;
    for(int i=0; i&lt;k; i++){
        int a,b,c;
        cin&gt;&gt;a&gt;&gt;b&gt;&gt;c;
        rule.push_back({a,b,c});
    }
    while (s&lt;=e)
    {
        long long cnt=0;
        int m = (s+e)/2;
        for(auto&amp;r : rule){
            if(m &lt; r.s) continue;
            cnt += (min(m,r.e)-r.s)/r.j + 1;
        }
        if(cnt&lt;d){
            s=m+1;
        }
        else{
            result=m;
            e=m-1;
        }
    }
    cout&lt;&lt;result;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1939] 중량제한]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1939-%EC%A4%91%EB%9F%89%EC%A0%9C%ED%95%9C</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1939-%EC%A4%91%EB%9F%89%EC%A0%9C%ED%95%9C</guid>
            <pubDate>Sat, 07 Sep 2024 15:36:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1939">https://www.acmicpc.net/problem/1939</a></p>
</blockquote>
<p>처음엔 크루스칼 알고리즘밖에 떠오르지 않았다. 가중치의 최댓값을 작고 점점 작게 탐색한다면... 쉽게 할 수 있을 것 같았다.
그러나! 나는 이분 탐색을 공부 중이기때문에, 이분 탐색으로 어케할지 이리저리 고민해봤다...</p>
<h4 id="파라메트릭-서치">파라메트릭 서치</h4>
<p>문제를 풀고 여러 블로그들을 돌아보니, 이런 종류의 문제에서 사용된 이분 탐색기법을 파라메트릭 서치라고 불렀다.</p>
<blockquote>
<p>파라메트릭 서치란 어떠한 정렬된 리스트에서 <strong>원하는 값</strong>을 찾는 이분 탐색과 달리** 조건에 부합하는 값을** 찾는 것이다.</p>
</blockquote>
<p>파라메트릭 서치를 사용하기 위해선 세가지 조건이 있다.</p>
<ul>
<li>조건을 만족하는 최대,최소를 구하는 문제여야 한다.</li>
<li>최댓값이 조건을 만족한다면, 최댓값보다 작은 값도 만족해야 한다.</li>
<li>답의 범위가 정수이거나 허용 오차 범위가 있어야 한다.</li>
</ul>
<h4 id="시간-복잡도">시간 복잡도</h4>
<ul>
<li>이진 탐색 $O(log(maxWeight))$ * bfs탐색(정점n + 간선 m) $O(n+m)$
=&gt; $O(nlogn)$</li>
</ul>
<h3 id="문제-풀이">문제 풀이</h3>
<ol>
<li>입력을 받으며 최대 가중치를 구함</li>
<li>최대 가중치와 최소 가중치의 중간 값을 구함</li>
<li>시작지점부터 bfs를 하며 간선을 이동<ul>
<li>만약 간선의 가중치가 2번에서 구한 값보다 크면 계속 움직이고 아니면 break</li>
</ul>
</li>
<li>도착지점 까지 움직였으면 최소 가중치를 2번 값 + 1</li>
<li>아니라면 최대 가중치를 2번 값 - 1</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;
using namespace std;

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

    int n, m;
    cin &gt;&gt; n &gt;&gt; m;

    vector&lt;vector&lt;pair&lt;int, int&gt;&gt;&gt; edge(n + 1); // [start] first:end second:w
    int max_weight = 0;

    for (int i = 0; i &lt; m; i++) {
        int a, b, c;
        cin &gt;&gt; a &gt;&gt; b &gt;&gt; c;
        edge[a].push_back({b, c});
        edge[b].push_back({a, c}); 
        max_weight = max(max_weight, c);
    }

    int s, e;
    cin &gt;&gt; s &gt;&gt; e;

    int min_weight = 1;

    while (min_weight &lt;= max_weight) {
        int current_weight = (min_weight + max_weight) / 2;

        vector&lt;bool&gt; visit(n + 1, false);
        visit[s] = true;

        queue&lt;int&gt; q;
        q.push(s);

        bool can_reach = false;

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

            if (start == e) {
                can_reach = true;
                break;
            }

            for (auto&amp; edg : edge[start]) {
                int next = edg.first;
                int w = edg.second;

                if (!visit[next] &amp;&amp; w &gt;= current_weight) {
                    visit[next] = true;
                    q.push(next);
                }
            }
        }

        if (can_reach) min_weight = current_weight + 1;
        else max_weight = current_weight - 1;
    }

    cout &lt;&lt; max_weight;

    return 0;
}</code></pre>
<p>참고
<a href="https://ialy1595.github.io/post/parametric-search/">https://ialy1595.github.io/post/parametric-search/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[json-server-auth를 이용한 로그인 구현 1]]></title>
            <link>https://velog.io/@ye_chan_/json-server-auth%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84-1</link>
            <guid>https://velog.io/@ye_chan_/json-server-auth%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84-1</guid>
            <pubDate>Sat, 07 Sep 2024 12:39:47 GMT</pubDate>
            <description><![CDATA[<h2 id="계기">계기</h2>
<p>프론트엔드를 개발하다보면 백엔드 api가 없어 개발에 난감한 경우가 있다.
그렇다고 openAPI만 사용하기엔 커스텀할 수 있는 내용이 적어 원하는 기능을 테스트 해보기가 어려운 순간도 있다. 그래서 가짜 api통신이 가능하도록 moke api를 만들려고 한다. 가짜 api를 만드는 방법은 여러가지가 있지만, 그중에서 json-server가 가장 간단하게 구현할 수 있다는 생각이 들어 채택했다.</p>
<h3 id="설치">설치</h3>
<pre><code>npm i json-server-auth json-server</code></pre><p>설치 후 파일 의존성이 잘 되어있는지 확인할 필요가 있다. 나는 처음에 에러가 떠서 뭔가 했는데 알고보니 의존성 오류였다..</p>
<h3 id="시작">시작</h3>
<p>우선 api의 json을 저장할 db.json 파일을 루트폴더에 만든다.
파일엔 유저를 저장할 users를 만든다.</p>
<pre><code class="language-js">{
  &quot;users&quot;: []
}</code></pre>
<p>앞으로 회원가입시 user에 유저들이 들어올 것이다.
pakege.json 파일의 skript에 명령어를 만든다.</p>
<pre><code class="language-js">  &quot;scripts&quot;: {
    &quot;server&quot;: &quot;json-server-auth --watch db.json --port 4444&quot;
  }</code></pre>
<p>watch 뒤에는 db가 저장된 json파일을 적고, port 뒤에는 서버를 열 포트 번호를 붙여준다. port를 붙이지 않으면 기본적으로 3000으로 열리는데, 리액트와 번호가 같으니 변경해주자.</p>
<h3 id="인증">인증</h3>
<p>json-server-auth의 경우 JWT로 인증이 가능하다.</p>
<h4 id="회원가입">회원가입</h4>
<p>셋중 어떤 routes를 써도 상관 없다.
<code>POST /register</code> <code>POST /signup</code> <code>POST /users</code>
register에는 아래와 같은 정보를 요구한다.</p>
<pre><code class="language-js">POST /register
{
  &quot;email&quot;: &quot;olivier@mail.com&quot;,
  &quot;password&quot;: &quot;bestPassw0rd&quot;
}</code></pre>
<p>회원가입시 자동으로 db.json에 값이 저장된다.</p>
<pre><code class="language-js">  &quot;users&quot;: [
    {
      &quot;email&quot;: &quot;test@test.com&quot;,
      &quot;password&quot;: &quot;$2a$10$YEM/FjW29ESgJenmfIE9t.HWQ0zQB5k.X1y9dtMifedhy4sXSI/0m&quot;,
      &quot;id&quot;: 1
    }
  ]</code></pre>
<h4 id="로그인">로그인</h4>
<p><code>POST /login</code> <code>POST /signin</code>
login 또한 이메일과 비밀번호를 필요로한다.</p>
<pre><code class="language-js">POST /login
{
  &quot;email&quot;: &quot;olivier@mail.com&quot;,
  &quot;password&quot;: &quot;bestPassw0rd&quot;
}</code></pre>
<p>JWT토큰을 전달받는다.</p>
<pre><code class="language-js">{
  &quot;accessToken&quot;: &quot;xxxx-xxxxx-xxxx&quot;,
  &quot;user&quot;: {
    &quot;email&quot;: &quot;test@test.com&quot;,
    &quot;id&quot;: 1
  }
}</code></pre>
<h3 id="구현">구현<img src="https://velog.velcdn.com/images/ye_chan_/post/0df92112-2956-41b3-ae1c-18f4a34230d4/image.png" alt=""></h3>
<pre><code class="language-js">import axios from &quot;axios&quot;;
import React, { useEffect, useState } from &quot;react&quot;;

function App() {
  const [token, setToken] = useState(&quot;&quot;);
  const [userId, setUserId] = useState(&quot;&quot;);
  const [email, setEmail] = useState(&quot;&quot;);
  const [password, setPassword] = useState(&quot;&quot;);
  const [signupEmail, setSignupEmail] = useState(&quot;&quot;);
  const [signupPassword, setSignupPassword] = useState(&quot;&quot;);


  const login = async () =&gt; {
    try {
      const { data } = await axios.post(&quot;http://localhost:4444/login&quot;, {
        email,
        password
      });
      console.log(data)
      setToken(data.accessToken);
      setUserId(data.user.id); 
      console.log(data.accessToken);
      alert(&quot;로그인 성공&quot;);
    } catch (error) {
      console.error(&quot;로그인 실패:&quot;, error.response?.data || error.message);
    }
  };

  const signup = async () =&gt; {
    try {
      await axios.post(&quot;http://localhost:4444/signup&quot;, {
        email: signupEmail,
        password: signupPassword
      });
      alert(&quot;회원가입이 완료되었습니다.&quot;);
      setSignupEmail(&quot;&quot;);
      setSignupPassword(&quot;&quot;);
    } catch (error) {
      console.error(&quot;회원가입 실패:&quot;, error.response?.data || error.message);
    }
  };

  return (
    &lt;&gt;

      {/* Signup Form */}
      &lt;div&gt;
        &lt;h2&gt;회원가입&lt;/h2&gt;
        &lt;input
          placeholder=&quot;이메일&quot;
          type=&quot;email&quot;
          value={signupEmail}
          onChange={(e) =&gt; setSignupEmail(e.target.value)}
        /&gt;
        &lt;input
          placeholder=&quot;비밀번호&quot;
          type=&quot;password&quot;
          value={signupPassword}
          onChange={(e) =&gt; setSignupPassword(e.target.value)}
        /&gt;
        &lt;button onClick={signup}&gt;회원가입&lt;/button&gt;
        &lt;br /&gt;
      &lt;/div&gt;

      {/* Login Form */}
      &lt;div&gt;
        &lt;h2&gt;로그인&lt;/h2&gt;
        &lt;input
          placeholder=&quot;이메일&quot;
          type=&quot;email&quot;
          value={email}
          onChange={(e) =&gt; setEmail(e.target.value)}
        /&gt;
        &lt;input
          placeholder=&quot;비밀번호&quot;
          type=&quot;password&quot;
          value={password}
          onChange={(e) =&gt; setPassword(e.target.value)}
        /&gt;
        &lt;button onClick={login}&gt;로그인&lt;/button&gt;
        &lt;br /&gt;
      &lt;/div&gt;


    &lt;/&gt;
  );
}

export default App;</code></pre>
<p>참고
<a href="https://www.npmjs.com/package/json-server-auth">https://www.npmjs.com/package/json-server-auth</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 2110] 공유기 설치]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2110-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2110-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 06 Sep 2024 04:25:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ye_chan_/post/b12f2ee7-466b-4d0d-945f-26aa9a20e880/image.png" alt=""></p>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/2110">https://www.acmicpc.net/problem/2110</a></p>
</blockquote>
<p><a href="https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2805-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0">나무자르기</a> 문제를 풀고 이진탐색을 좀 집중공략할 필요가 있다고 생각이 들어서 당분간 이진탐색 문제를 집중적으로 풀려고 한다.
이 문제의 경우 아까와 비슷한 방식으로 이진탐색 &gt; n번 탐색 의 알고리즘을 가진다.</p>
<h3 id="풀이-방법">풀이 방법</h3>
<ol>
<li>집의 좌표를 정렬한다.</li>
<li>좌표의 간격의 최댓값과 최솟값을 구한다.<ul>
<li>최소 간격: 1</li>
<li>최대 간격: 좌표가 제일 큰 집 - 제일 작은 집</li>
</ul>
</li>
<li>간격을 이분탐색하며 적절한 간격을 구한다.<ul>
<li>만약 집 간의 간격이 이분탐색의 mid값보다 크면 cnt++</li>
<li>cnt가 m보다 많다면 result = mid(현재까지의 최대 간격)<ul>
<li>min값을 mid로 설정하고 더 큰 간격이 있는지 이진탐색 시작     </li>
</ul>
</li>
<li>cnt가 m보다 작다면 max=mid로 설정하고 더 좁은 간격에서 m만큼의 공유기가 설치된 간격을 찾음</li>
</ul>
</li>
</ol>
<h4 id="시간-복잡도">시간 복잡도</h4>
<p>이진탐색($logm$) * n
=$O(nlogn)$</p>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;

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

    int n;
    int m;
    int house[200000];

    cin &gt;&gt; n &gt;&gt; m;
    for(int i = 0; i &lt; n; i++){
        cin &gt;&gt; house[i];
    }
    sort(house, house + n);

    int min = 1, max = house[n-1] - house[0];
    int result = 0;

    while (min &lt;= max) {
        int mid = (min + max) / 2;
        int current = house[0];
        int cnt = 1; 

        for(int i = 1; i &lt; n; i++){
            if(house[i] - current &gt;= mid) {
                cnt++;
                current = house[i];
            }
        }

        if(cnt &gt;= m){
            result = mid;
            min = mid + 1;
        } else {
            max = mid - 1;
        }
    }

    cout &lt;&lt; result;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 2805] 나무 자르기]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2805-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2805-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Fri, 06 Sep 2024 03:10:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ye_chan_/post/cf01de8b-cc13-40bb-86ff-f0fa28b6b4ef/image.png" alt=""></p>
<blockquote>
<p><a href="https://www.acmicpc.net/problem/2805">https://www.acmicpc.net/problem/2805</a></p>
</blockquote>
<p>예전에 class3 에센셜 문제를 다 풀었었는데, 새롭게 한 문제가 생겼길래 풀어봤다.
제일 까다로웠던 부분이 나무의 길이가 &#39;넘침&#39;&gt;&#39;적음&#39;&gt;&#39;넘침&#39;으로 갈 때 종료 조건을 어떻게 할까였는데, 생각하다가 max-min&gt;1일때(예를 들어 13~15면 middle이 14로 고정이므로)로 설정했다.</p>
<ul>
<li>참고로 다른 사람들은 (min &lt; max)로 값이 엇갈리는지 확인하는 방식도 사용했다. 왜 나는 문제 풀면서 이리 쉬운 생각을 못했을까.. 이진 탐색 연습이 많이 필요해보인다.<h3 id="풀이-과정">풀이 과정</h3>
</li>
</ul>
<ol>
<li>나무의 최댓값을 찾는다.</li>
<li>최댓값과 최솟값의 중간 값 만큼 나무를 자른다.</li>
<li>자른 나무가 m보다 크다면 min을 middle로 바꾼다.</li>
<li>자른 나무가 m보다 작으면 max를 middle로 바꾼다.</li>
<li>max-min이 1보다 작거나 같을때까지 반복한다.<ul>
<li>max-min이 1이 되면 middle이 정해지기 때문에 더 할 필요가 없다.</li>
</ul>
</li>
</ol>
<h4 id="시간-복잡도">시간 복잡도</h4>
<p>이진탐색($logm$) * 나무의 개수($n$)
= $O(nlogm)$</p>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;algorithm&gt;

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    int n;
    long long m;
    int arr[1000000];

    cin&gt;&gt;n&gt;&gt;m;
    for(int i=0; i&lt;n; i++){
        cin&gt;&gt;arr[i];
    }
    int min=0,max=*max_element(arr, arr + n);// 최대 나무 길이 찾기
    while (max-min&gt;1) // 중간 값이 고정 됨
    {  
        int middle = (min+max)/2;
        long long cnt=0;
        for(int i=0; i&lt;=n-1; i++){ // 나무 길이 합하기
            if(arr[i]-middle&gt;0) cnt+= arr[i]-middle;
        }
        if(cnt == m) break;
        else if (cnt &gt; m){ // m보다 합이 크면 &gt;&gt; 더 조금 잘라야 함
            min = middle;
        }
        else { //m 보다 합이 작으면 &gt;&gt; 더 많이 잘라야 함
            max = middle;
        }
    }
    cout&lt;&lt;(min+max)/2; 

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 2143] 두 배열의 합]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2143-%EB%91%90-%EB%B0%B0%EC%97%B4%EC%9D%98-%ED%95%A9</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-2143-%EB%91%90-%EB%B0%B0%EC%97%B4%EC%9D%98-%ED%95%A9</guid>
            <pubDate>Tue, 03 Sep 2024 03:46:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/2143">https://www.acmicpc.net/problem/2143</a></p>
</blockquote>
<p>나는 투 포인터를 활용해서 풀었는데, 이진탐색을 사용해서 푼 사람들도 많았다.
시간복잡도는 거의 동일하니 편한 방법을 쓰면 될 것 같다. 
<img src="https://velog.velcdn.com/images/ye_chan_/post/eae08525-73d3-4d70-aeb2-547cdd6701bb/image.png" alt="">소요 시간만 놓고 보면 투포인터가 조금 더 빠르게 찍혔는데, 코드 길이 생각하면 그냥 이진탐색 쓰고 말듯.
    + 해시맵 방법도 있어서 해봤는데 구현 속도도, 시간복잡도도 이진탐색이 좋은 것 같다.</p>
<h4 id="시간-복잡도">시간 복잡도</h4>
<p>O($n^2 logm$) 
-&gt; 아래에서 자세히 설명</p>
<h2 id="투-포인터">투 포인터</h2>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li>A부분합과 B부분합을 모두 구한다.</li>
<li>A부분합을 오름차순, B부분합을 내림차순 한다.</li>
<li>A부분합+B부분합이 t가 되는 경우를 구한다.</li>
<li>만약 t가 된다면 t가 되는 A부분합의 개수 * B 부분합의 개수를 count에 더한다.<ul>
<li>경우의 수를 구하는 것 이기 때문</li>
</ul>
</li>
<li>만약 t보다 크다면 B부분합의 idx--, t보다 작다면 A부분합의 idx++<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
</code></pre>
</li>
</ol>
<p>using namespace std;</p>
<p>int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    int T, n, m;
    cin &gt;&gt; T &gt;&gt; n;</p>
<pre><code>int A[1000];
for (int i = 0; i &lt; n; i++) {
    cin &gt;&gt; A[i];
}
cin &gt;&gt; m;
int B[1000];
for (int i = 0; i &lt; m; i++) {
    cin &gt;&gt; B[i];
}

// A의 모든 부분 배열의 합 계산
vector&lt;int&gt; sumA;
for (int i = 0; i &lt; n; i++) {
    int current_sum = 0;
    for (int j = i; j &lt; n; j++) {
        current_sum += A[j];
        sumA.push_back(current_sum);
    }
}

// B의 모든 부분 배열의 합 계산
vector&lt;int&gt; sumB;
for (int i = 0; i &lt; m; i++) {
    int current_sum = 0;
    for (int j = i; j &lt; m; j++) {
        current_sum += B[j];
        sumB.push_back(current_sum);
    }
}

// sumA는 오름차순, sumB는 내림차순으로 정렬
sort(sumA.begin(), sumA.end());
sort(sumB.begin(), sumB.end(), greater&lt;int&gt;());

long long count = 0;
int i = 0, j = 0;

// 투 포인터를 이용한 두 리스트의 합 비교
while (i &lt; sumA.size() &amp;&amp; j &lt; sumB.size()) {
    int a = sumA[i];
    int b = sumB[j];
    int sum = a + b;

    if (sum == T) {
        long long countA = 0;
        long long countB = 0;

        // sumA[i]와 같은 값의 개수를 모두 카운트
        while (i &lt; sumA.size() &amp;&amp; sumA[i] == a) {
            countA++;
            i++;
        }

        // sumB[j]와 같은 값의 개수를 모두 카운트
        while (j &lt; sumB.size() &amp;&amp; sumB[j] == b) {
            countB++;
            j++;
        }

        // 두 개수의 곱만큼 결과에 더하기 (쌍을 구하는 것 이므로 곱함)
        count += countA * countB;
    } else if (sum &lt; T) {
        i++;
    } else {
        j++;
    }
}

cout &lt;&lt; count;
return 0;</code></pre><p>}</p>
<pre><code>
## 이진 탐색

### 풀이 과정
1. A부분합과 B부분합을 구한다.
2. B부분합을 정렬한다.
3. t-A부분합을 저장한다.
4. t-A==B부분합이 되는 첫 지점과, 초과하는 부분을 찾는다(같은 부분합의 개수 구하기 위해)
5. count에 초과하는 부분 - 첫 지점을 한다.
### 구현
```cpp
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    int T, n, m;
    cin &gt;&gt; T &gt;&gt; n;

    int A[1000];
    for (int i = 0; i &lt; n; i++) {
        cin &gt;&gt; A[i];
    }
    cin &gt;&gt; m;
    int B[1000];
    for (int i = 0; i &lt; m; i++) {
        cin &gt;&gt; B[i];
    }

    // A의 모든 부분 배열의 합 계산
    vector&lt;int&gt; sumA;
    for (int i = 0; i &lt; n; i++) {
        int current_sum = 0;
        for (int j = i; j &lt; n; j++) {
            current_sum += A[j];
            sumA.push_back(current_sum);
        }
    }

    // B의 모든 부분 배열의 합 계산
    vector&lt;int&gt; sumB;
    for (int i = 0; i &lt; m; i++) {
        int current_sum = 0;
        for (int j = i; j &lt; m; j++) {
            current_sum += B[j];
            sumB.push_back(current_sum);
        }
    }

    long long count = 0;   
    sort(sumB.begin(), sumB.end());
    for(auto&amp; a : sumA){
        int tmp = T-a;
        auto l = lower_bound(sumB.begin(), sumB.end(), tmp);//tmp와 같은 첫 값
        auto r = upper_bound(sumB.begin(), sumB.end(), tmp);//tmp 첫 초과값
        count += (r-l);
    }

    cout &lt;&lt; count;
    return 0;
}
</code></pre><h2 id="시간-복잡도-1">시간 복잡도</h2>
<ul>
<li>이진 탐색</li>
</ul>
<p>$m^2$번 정렬($logm$)하므로 O($m^2 log m$)</p>
<pre><code class="language-cpp">sort(sumB.begin(), sumB.end());</code></pre>
<p>sumA번($n^2$) sumB($m^2$)을 이진탐색 하므로 O($n^2 log{m}$ )</p>
<pre><code class="language-cpp">for(auto&amp; a : sumA){
    int tmp = T-a;
    auto l = lower_bound(sumB.begin(), sumB.end(), tmp);
    auto r = upper_bound(sumB.begin(), sumB.end(), tmp);
    count += (r-l);
}</code></pre>
<p>=&gt; O($(n^2+m^2)log{m}$)</p>
<ul>
<li>투 포인터</li>
</ul>
<p>$n^2$번 정렬하고 $m^2$번 정렬 하므로 O($n^2 log n + m^2 log m$)</p>
<pre><code class="language-cpp">sort(sumA.begin(), sumA.end());
sort(sumB.begin(), sumB.end());</code></pre>
<p>투 포인터의 경우</p>
<p>0<del>$n^2$까지, $m^2$</del>0까지 이므로 O($n^2+m^2$)이므로 영향 X</p>
<pre><code class="language-cpp">int i = 0, j = sumB.size() - 1;

while (i &lt; sumA.size() &amp;&amp; j &gt;= 0) {
    int sum = sumA[i] + sumB[j];

    if (sum == T) {
        long long countA = 0, countB = 0;
        int a = sumA[i], b = sumB[j];

        while (i &lt; sumA.size() &amp;&amp; sumA[i] == a) {
            countA++;
            i++;
        }

        while (j &gt;= 0 &amp;&amp; sumB[j] == b) {
            countB++;
            j--;
        }

        count += countA * countB;
    } else if (sum &lt; T) {
        i++;
    } else {
        j--;
    }
}</code></pre>
<p>=&gt;O($n^2 log n + m^2 log m$)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1509] 팰린드롬 분할]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1509-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%B6%84%ED%95%A0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1509-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%B6%84%ED%95%A0</guid>
            <pubDate>Mon, 02 Sep 2024 10:51:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1509">https://www.acmicpc.net/problem/1509</a></p>
</blockquote>
<p>무려 DP를 2번이나 구현해야하는 문제이다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li><p>팰린드롬 구하기
 palindrome[i][j] = palindrome[i + 1][j - 1];</p>
<ul>
<li>문자열의 범위(0~len)일때 ,구하려는 범위의 시작을 i, 끝을 j로 두자</li>
<li>범위가 1일때: 무조건 true</li>
<li>범위가 2일때: 두글자가 모두 같으면 true</li>
<li>범위가 3이상 일때:양끝 글자가 같고, 그 속 글자들이 모두 같을때 (ㅁOOOOㅁ)
양 끝 안의 값이 같은지 어떻게 알지? -&gt; i+1~j-1까지가 성립하는지 확인하면 됨</li>
</ul>
</li>
<li><p>최소 분할 개수 구하기
 ans[j] = min(ans[j], ans[i-1] + 1)</p>
<ul>
<li>0~i까지의 범위 중 ture인 값을 구하기</li>
<li>0~i까지 ture라면? 쪼개진게 없으므로 1</li>
<li>j<del>i까지 ture라면? j까지 쪼개진거 + 1 (j+1</del>i까지 새롭게 쪼개짐)</li>
</ul>
</li>
<li><p>len-1의 최소 분할 개수 출력</p>
</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;climits&gt; 

using namespace std;

int main() {

    string s;
    cin &gt;&gt; s;
    int l = s.length();
    bool palindrome[2501][2501] = {false};
    for (int i = 0; i &lt; l; i++) { // 1개일때 모두 트루
        palindrome[i][i] = true;  
    }

    for (int j = 1; j &lt; l; j++) {
        for (int i = 0; i &lt; j; i++) {
            if (s[i] == s[j]) {
                if (j - i == 1) {
                    palindrome[i][j] = true; // 2개일때(aa) 
                } else {
                    palindrome[i][j] = palindrome[i + 1][j - 1]; //OㅁㅁㅁㅁㅁO
                }
            }
        }
    }

    vector&lt;int&gt; ans(l, INT_MAX);  // 0부터 n까지의 최소 분할 개수 저장
    for (int j = 0; j &lt; l; j++) {
        if (palindrome[0][j]) ans[j] = 1;  // 0~j까지가 true면 1로 끝.
        else {
            for (int i = 1; i &lt;= j; i++) {
                if (palindrome[i][j]) ans[j] = min(ans[j], ans[i-1] + 1); // 분할됐으므로 +1
            }
        }
    }

    cout &lt;&lt; ans[l - 1]; 

    return 0;
}
</code></pre>
<p>참고
<a href="https://byeo.tistory.com/entry/boj-1509-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%B6%84%ED%95%A0">https://byeo.tistory.com/entry/boj-1509-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%B6%84%ED%95%A0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 9328] 열쇠]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-9328-%EC%97%B4%EC%87%A0</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-9328-%EC%97%B4%EC%87%A0</guid>
            <pubDate>Sat, 31 Aug 2024 16:15:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/9328">https://www.acmicpc.net/problem/9328</a></p>
</blockquote>
<p>알고리즘의 묘미랄까.. 다 풀고 남들의 코드를 보는데 나와 다른 부분이 있어 흥미로웠다.</p>
<ol>
<li>시작 지점을 구하는 방법<ul>
<li>내 방법: 입력을 받으며 테두리일 경우 &#39;.&#39;인 경우 따로 저장</li>
<li>찾은 방법: &#39;.&#39;으로만 이루어진 테두리를 하나 만들어 감싼다.
예) (3,3)이 있다면, (4,4)로 만들고 테두리들을 모두&#39;.&#39;으로 채운다</li>
</ul>
</li>
<li>잠긴 문을 만났을 때, 어떻게 할 것인가<ul>
<li>내 방법: 일단 스탑하고, 열쇠를 만난다면 그동안의 visited를 초기화 하고 다시 시작한다.</li>
<li>찾은 방법: 따로 저장해 뒀다가 열쇠를 찾으면 저장해 뒀던 것을 큐에 옮긴다.</li>
</ul>
</li>
</ol>
<p>즉, 내 알고리즘의 시간복잡도는 최악의 경우 키의 개수인 26 * 맵의 크기인 nm 이었고, 찾은 방법은 최악의 경우 nm이었다. 결국 상수곱이므로 크게 차이는 나지 않지만, 그래도 좀 더 좋은 알고리즘이 있다면 그 방법을 택하는게 좋은거니 찾은 방법을 기준으로 서술하겠다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li>테두리를 &#39;.&#39;으로 채운 뒤 (1,1)부터 값을 입력 받는다.</li>
<li>테두리를 시작 값으로 bfs를 실행한다. 만약 &#39;$&#39;를 찾으면 cnt를 증가시킨다.</li>
<li>만약 문이 잠겨 있다면 queue[알파벳 번호]에 문의 좌표를 저장해 둔다.</li>
<li>만약 키를 찾았다면 queue[알파벳 번호]에 저장되어 있던 좌표들을 bfs에 꺼낸다.</li>
<li>bfs 큐가 빌 때까지 반복한다.</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;string&gt;
#include &lt;cstring&gt;  
using namespace std;

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

    int t;
    cin &gt;&gt; t;
    while (t--) {
        int n, m;
        cin &gt;&gt; n &gt;&gt; m;
        //map과 visited 초기화
        char map[102][102];  
        bool visited[102][102]; 
        memset(visited, false, sizeof(visited)); 
        memset(map, &#39;.&#39;, sizeof(map));  

        //map 입력 - 테두리를 제외해 1부터 n까지에 채운다
        for (int i = 1; i &lt;= n; i++) {
            for (int j = 1; j &lt;= m; j++) {
                cin &gt;&gt; map[i][j];
            }
        }


        // Key 설정
        string key;
        cin &gt;&gt; key;
        bool keyArr[26] = {false};
        if (key != &quot;0&quot;) {
            for (char c : key) {
                keyArr[c - &#39;a&#39;] = true;
            }
        }

        // BFS 
        int move[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
        queue&lt;pair&lt;int, int&gt;&gt; q;
        queue&lt;pair&lt;int, int&gt;&gt; door[26];

        q.push({0,0});
        visited[0][0] = true;

        int cnt = 0;
        while (!q.empty()) {
            int i = q.front().first;
            int j = q.front().second;
            q.pop();

            for (auto&amp; mv : move) {
                int mi = i + mv[0];
                int mj = j + mv[1];
                if (mi &lt; 0 || mi &gt; n+1 || mj &lt; 0 || mj &gt; m+1) continue;
                if (map[mi][mj] != &#39;*&#39; &amp;&amp; !visited[mi][mj]) {
                    char next = map[mi][mj];
                    if (next == &#39;$&#39;) {
                        cnt++;
                    }
                    else if (&#39;A&#39; &lt;= next &amp;&amp; next &lt;= &#39;Z&#39;) { // 문을 만날 경우
                        if (!keyArr[next - &#39;A&#39;]) {  // 키가 존재하지 않을 경우
                            door[next - &#39;A&#39;].push({mi, mj}); 
                            continue;  
                        }
                    }
                    else if (&#39;a&#39; &lt;= next &amp;&amp; next &lt;= &#39;z&#39;) { // 키를 만날 경우
                        if (!keyArr[next - &#39;a&#39;]) {
                            keyArr[next - &#39;a&#39;] = true;
                            while (!door[next - &#39;a&#39;].empty()) {
                                q.push(door[next - &#39;a&#39;].front());
                                door[next - &#39;a&#39;].pop();
                            }
                        }
                    }

                    q.push({mi, mj});
                    visited[mi][mj] = true;
                }
            }
        }

        cout &lt;&lt; cnt &lt;&lt; &#39;\n&#39;; 
    }

    return 0;
}</code></pre>
<p>참고
<a href="https://yabmoons.tistory.com/97">https://yabmoons.tistory.com/97</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1644] 소수의 연속합]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1644-%EC%86%8C%EC%88%98%EC%9D%98-%EC%97%B0%EC%86%8D%ED%95%A9</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1644-%EC%86%8C%EC%88%98%EC%9D%98-%EC%97%B0%EC%86%8D%ED%95%A9</guid>
            <pubDate>Wed, 28 Aug 2024 13:45:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1644">https://www.acmicpc.net/problem/1644</a></p>
</blockquote>
<p>소수를 구하고 누적합과 투포인터를 사용하면 쉽게 풀 수 있는 문제이다.
소수를 효과적으로 구하는 방법을 찾아 보니 <a href="https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4">에라토스테네스의 체</a>라는 방법이 있었다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li>에라토스테네스의 체를 활용해 소수를 구한다.</li>
<li>n까지의 소수들을 누적합 한다.</li>
<li>투포인터를 사용하여 구간합을 구한다.(끝 값의 누적합 - 시작 값의 누적합)<ul>
<li>끝 값을 증가시키면 값이 커지고, 시작 값을 증가시키면 값이 작아진다.</li>
</ul>
</li>
<li>구간 합을 n과 비교한다.<ul>
<li>n과 같으면 cnt++ 하고 끝 값을 증가 시킨다.</li>
<li>누적 합이 n보다 크다면 시작 값을 증가 시킨다.</li>
<li>누적 합이 n보다 작으면 끝 값을 증가 시킨다.</li>
</ul>
</li>
<li>끝 값이 소수들의 개수보다 커지면 종료한다.</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    int n;
    cin &gt;&gt; n;

    if (n &lt;= 1) {
        cout &lt;&lt; 0;
        return 0;
    }

    vector&lt;bool&gt; Pri(n + 1, true); 
    Pri[0] = Pri[1] = false; 

    for (int i = 2; i * i &lt;= n; i++) {
        if (Pri[i]) {
            for (int j = i * i; j &lt;= n; j += i) {
                Pri[j] = false;
            }
        }
    }

    vector&lt;int&gt; sums;
    sums.push_back(0); 

    for (int i = 2; i &lt;= n; i++) {
        if (Pri[i]) {
            sums.push_back(sums.back() + i);
        }
    }

    int s = 0, e = 0;
    int cnt = 0;

    while (e &lt; sums.size()) {
        int sum = sums[e] - sums[s];

        if (sum == n) {
            cnt++;
            e++;
        } else if (sum &lt; n) {
            e++;
        } else {
            s++;
        }
    }

    cout &lt;&lt; cnt;
    return 0;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1202] 보석 도둑]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1202-%EB%B3%B4%EC%84%9D-%EB%8F%84%EB%91%91</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1202-%EB%B3%B4%EC%84%9D-%EB%8F%84%EB%91%91</guid>
            <pubDate>Mon, 26 Aug 2024 09:42:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1202">https://www.acmicpc.net/problem/1202</a></p>
</blockquote>
<p>으아아 아직 그리디 문제에 대한 감이 부족한 것 같다. 그리디 문제를 좀 많이 풀어봐야겠다.</p>
<p>보석의 무게와 배냥의 무게를 정렬해야겠다는 생각까지는 하였으나, 가치를 어떻게 정렬 해야할지 고민을 제대로 하지 않고 처음엔 보석과 배낭의 크기를 정렬하고 하나씩 증가하며 비교를 하려고 했는데, 양이 무지하게 많아 시간초과가 되었고, 풀이를 보니 기존 계산한 값을 우선순위 큐에 저장하는 그리디 문제였다.</p>
<p>시간 복잡도로 생각해보면, 우선순위 큐를 사용하지 않을 경우 n번의 보석을 k번 비교하므로 O(NK)이고, 우선순위 큐를 사용할 경우, O(logN)을 N <code>(idx&lt;n)</code> 번 반복하므로 O(NlogN)이 된다.</p>
<h3 id="풀이과정">풀이과정</h3>
<ol>
<li>보석과 가방을 오름차순으로 정렬한다.</li>
<li>가방의 용량이 작은 것 부터, 들어갈 수 있는 모든 보석들을 우선순위 큐에 저장한다.<ul>
<li>용량이 큰 가방은 작은 가방이 넣을 수 있는 보석을 모두 넣을 수 있기 때문이다.</li>
</ul>
</li>
<li>우선순위 큐에서 보석 하나를 빼 작은 가방에 넣는다.</li>
</ol>
<blockquote>
<p>예)
보석의 무게가 {1} {2} {3} {4} 라면, 무게가 2인 가방은 {1},{2}를 큐에 넣고 이들 중 가치가 큰 보석을 하나 빼온다. 그 후 무게가 4인 가방은 이미 있는 큐에 {3} {4}의 무게를 가진 보석을 넣고 그들 중 가장 가치가 큰 보석을 꺼낸다.</p>
</blockquote>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;

using namespace std;

int main() {
    int n, k;
    cin &gt;&gt; n &gt;&gt; k;

    vector&lt;pair&lt;int, int&gt;&gt; jewel(n);
    vector&lt;int&gt; bag(k);

    for (int i = 0; i &lt; n; i++) {
        cin &gt;&gt; jewel[i].first &gt;&gt; jewel[i].second;
    }

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

    sort(jewel.begin(), jewel.end());
    sort(bag.begin(), bag.end());

    priority_queue&lt;int&gt; pq;
    long long sum = 0;
    int idx = 0;

    for (int i = 0; i &lt; k; i++) {
        while (idx &lt; n &amp;&amp; jewel[idx].first &lt;= bag[i]) {
            pq.push(jewel[idx++].second);
        }
        if (!pq.empty()) {
            sum += pq.top();
            pq.pop();
        }
    }

    cout &lt;&lt; sum;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 1005] ACM Craft]]></title>
            <link>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1005-ACM-Craft</link>
            <guid>https://velog.io/@ye_chan_/%EB%B0%B1%EC%A4%80-1005-ACM-Craft</guid>
            <pubDate>Sun, 25 Aug 2024 12:17:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1005">https://www.acmicpc.net/problem/1005</a></p>
</blockquote>
<p>위상정렬을 이용한 문제이다.</p>
<p>도착 위치가 3 일때,<code>{1}-&gt;{3}</code> 으로 바로 가는 경우와 <code>{1}-&gt;{2}-&gt;{3}</code>으로 다른 노드를 거쳐 가는 경우가 존재할 때, 이를 어떻게 해결할지만 조심하면 바로 풀리는 문제이다.</p>
<h3 id="풀이-과정">풀이 과정</h3>
<ol>
<li><p>현재 노드 까지의 최솟값을 구하기 위해선, 해결해야 할 노드들을 전부 <strong>동시</strong>에 해결해야 한다.
⇒ 위상정렬 알고리즘 사용</p>
</li>
<li><p>현재 노드의 최솟값 해결해야 할 노드들 중, 가장 오래 걸린 노드 + 현재 노드를 계산 한다.(모든 노드를 동시에 해결한다면, 제일 오래 걸린 노드가 완료될 때까지 기다려야 하기 때문)
⇒ 기존에 있던 값을 활용하므로 <strong>다이나믹 프로그래밍</strong></p>
</li>
</ol>
<h3 id="구현">구현</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;

using namespace std;

int main()
{

    int t;
    cin&gt;&gt;t;
    while (t--)
    {
        int n,k;
        int build[1001]; // 짓는데 소요 시간
        int result[1001];
        int in[1001] = {0,}; // 진입 차수
        vector&lt;vector&lt;int&gt;&gt; edge(1000, vector&lt;int&gt;());
        cin&gt;&gt;n&gt;&gt;k;
        for(int i=1; i&lt;=n; i++){
            cin&gt;&gt;build[i];
            result[i] = build[i];
        }
        for(int i=1; i&lt;=k; i++){
            int s, e;
            cin&gt;&gt;s&gt;&gt;e;
            edge[s].push_back(e);
            in[e] ++;
        }
        int w;
        cin &gt;&gt; w;

        queue &lt;int&gt; q;
        for(int i=1; i&lt;=n; i++){
            if(in[i] == 0){
                q.push(i);
            }
        }

        while (!q.empty())
        {
            int s = q.front();
            q.pop();
            for(auto&amp; e : edge[s]){
                result[e] = max(result[e], result[s] + build[e]);
                if(--in[e] == 0){
                    q.push(e);
                }

            }
        }
        cout&lt;&lt;result[w]&lt;&lt;&quot;\n&quot;;

    }




    return 0;
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>