<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko-KR"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://hadol2.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hadol2.github.io/" rel="alternate" type="text/html" hreflang="ko-KR" /><updated>2026-04-16T17:57:43+09:00</updated><id>https://hadol2.github.io/feed.xml</id><title type="html">Hadol_s</title><subtitle>Hadol&apos;s programming blog — Dev, Projects, PS</subtitle><entry><title type="html">벨만-포드 알고리즘이란?</title><link href="https://hadol2.github.io/posts/%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80/" rel="alternate" type="text/html" title="벨만-포드 알고리즘이란?" /><published>2026-04-16T00:00:00+09:00</published><updated>2026-04-16T00:00:00+09:00</updated><id>https://hadol2.github.io/posts/%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80</id><content type="html" xml:base="https://hadol2.github.io/posts/%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80/"><![CDATA[<h2 id="핵심-요약">핵심 요약</h2>

<p>음수 간선이 있는 그래프에서 최단경로를 구하거나 <strong>음수 사이클 존재 여부</strong>를 판단할 때 쓴다. 다익스트라가 안 되는 상황에서 꺼내는 카드.</p>

<hr />

<h2 id="개념-설명">개념 설명</h2>

<h3 id="벨만-포드-알고리즘">벨만-포드 알고리즘</h3>

<p>다익스트라는 매 단계에서 현재 가장 가까운 노드를 선택해 탐색한다. 덕분에 빠르지만, 음수 간선이 있으면 이미 “확정”된 노드가 나중에 더 짧은 경로로 도달될 수 있어서 틀린 답을 낸다.</p>

<p>벨만-포드는 반대로 매 라운드마다 <strong>모든 간선을 전부 확인</strong>하며 dist를 갱신한다. 느리지만(O(VE)) 음수 간선도 처리할 수 있다.</p>

<p><strong>작동 원리</strong>: N개의 정점을 잊는 단순 경로에는 최대 N-1개의 간선이 존재한다. 따라서 모든 간선을 N-1번 확인하면 모든 최단경로가 확정된다.</p>

<p><strong>언제 쓰나?</strong></p>

<ul>
  <li>그래프에 음수 가중치 간선이 포함된 경우</li>
  <li>음수 사이클 존재 여부를 확인해야 하는 경우</li>
  <li>시작점이 정해지지 않아 “어느 경로에서든” 사이클 확인이 필요한 경우</li>
</ul>

<p><strong>핵심 패턴 (C++)</strong></p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dist</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">INF</span><span class="p">);</span>
<span class="n">dist</span><span class="p">[</span><span class="n">start</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">e</span> <span class="o">:</span> <span class="n">edges</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">!=</span> <span class="n">INF</span> <span class="o">&amp;&amp;</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* 음수 사이클 */</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="음수-사이클-감지-원리">음수 사이클 감지 원리</h3>

<p>N-1번 반복으로 최단경로가 이미 확정됐다면, N번째 라운드에서는 어떤 dist도 갱신되면 안 된다. 만약 갱신된다면? 그 경로 어딘가에 <strong>계속 돌수록 비용이 줄어드는 음수 사이클</strong>이 존재한다는 뜻이다.</p>

<hr />

<h2 id="이-문제에서의-적용">이 문제에서의 적용</h2>

<p><strong>BOJ 1865 웜홀에서 어떻게 썼나?</strong></p>

<p>이 문제의 핵심 관찰은 두 가지다:</p>

<ol>
  <li><strong>“시작점이 지정되지 않는다”</strong> — 어느 지점에서 출발해도 시간이 역행하는 경로가 있으면 YES다. 따라서 특정 시작점에서 벨만-포드를 돌리는 게 아니라, <strong>모든 정점을 시작점으로 간주</strong>해야 한다. 이를 구현하는 가장 쉬운 방법이 <code class="language-plaintext highlighter-rouge">dist</code>를 INF가 아닌 <strong>0으로 초기화</strong>하는 것이다 — 마치 모든 점에서 동시에 시작하는 것처럼.</li>
  <li><strong>도로는 양방향, 웜홀은 단방향 음수</strong> — 도로는 <code class="language-plaintext highlighter-rouge">edges</code>에 양방향으로 두 번 추가, 웜홀은 <code class="language-plaintext highlighter-rouge">-cost</code>로 단방향 추가.</li>
</ol>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="c1">// dist를 0으로 초기화 = 모든 정점에서 동시 출발 효과</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dist</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kt">bool</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">e</span> <span class="o">:</span> <span class="n">edges</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>INF 체크 없애는 이유</strong>: 일반 벨만-포드에선 <code class="language-plaintext highlighter-rouge">dist[u] != INF</code> 조건을 달지만, 웜홀은 dist가 모두 0이라 INF인 노드가 없다. 오히려 INF 조건을 달면 연결되지 않은 컴포넌트의 음수 사이클을 못 잡는 반례가 생긴다.</p>

<hr />

<h2 id="주의할-점--흔한-실수">주의할 점 / 흔한 실수</h2>

<ul>
  <li><strong>dist INF 초기화 vs 0 초기화</strong>: 단일 출발점 최단경로 → INF 초기화. 전체 그래프 음수 사이클 감지 → 0 초기화. 헷갈리지 말 것.</li>
  <li><strong>INF 체크 조건 누락/추가 오류</strong>: 웜홀체럼 dist를 0으로 초기화하면 INF 비교 조건이 필요 없다. 반대로 INF 초기화 문제에서 INF 체크 빼면 오버플로우 발생.</li>
  <li><strong>오버플로우</strong>: N=500, E=6000, cost=-10000이면 최악 누적값이 int 범위를 초과할 수 있다. 필요시 long long 사용.</li>
  <li><strong>사이클 감지 시 N vs N+1번 반복</strong>: N번 돌면서 i==N-1일 때 갱신 체크하는 방식과, N-1번 돌고 한 번 더 검사하는 방식 둘 다 동일하다.</li>
  <li><strong>무방향 그래프</strong>: 도로 같은 무방향 간선은 반드시 양방향으로 두 번 추가.</li>
</ul>

<hr />

<h2 id="이런-패턴이면-벨만-포드를-의심하라">이런 패턴이면 벨만-포드를 의심하라</h2>

<p>문제를 보자마자 벨만-포드를 떠올려야 하는 시그널들:</p>

<p><strong>키워드/조건 시그널</strong></p>

<ul>
  <li>“음수 가중치”가 명시된 그래프에서 최단거리 구하기</li>
  <li>“시간이 거꾸로 간다”, “비용이 줄어든다”, “웜홀” 같은 표현</li>
  <li>“음수 사이클이 존재하는지 판단하라”</li>
  <li>“출발점에서 다시 출발점으로 돌아왔을 때 값이 줄어드는가”</li>
  <li>시작점이 고정되지 않고 “어느 지점에서 출발해도” 성립하는지</li>
</ul>

<p><strong>구조적 시그널</strong></p>

<ul>
  <li>간선 가중치에 음수가 포함될 수 있다 (문제에서 명시하거나, 값 범위에 음수 포함)</li>
  <li>무방향 그래프 + 웜홀(단방향 음수 간선)이 섞여 있음</li>
  <li>정점 수 N이 500 이하, 간선 수 E가 수천 이하 → O(VE) 허용 범위</li>
  <li>플로이드-워셜은 N이 너무 크거나(N&gt;500), 단일 출발 최단경로만 필요할 때 벨만-포드 선택</li>
</ul>

<p><strong>다익스트라 vs 벨만-포드 즉시 판단 기준</strong></p>

<table>
  <thead>
    <tr>
      <th>상황</th>
      <th>선택</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>음수 간선 없음</td>
      <td>다익스트라</td>
    </tr>
    <tr>
      <td>음수 간선 있음, 사이클 감지 불필요</td>
      <td>벨만-포드 또는 SPFA</td>
    </tr>
    <tr>
      <td>음수 사이클 존재 여부 확인 필요</td>
      <td>벨만-포드 (필수)</td>
    </tr>
    <tr>
      <td>전체 쌍 최단거리, N ≤ 500</td>
      <td>플로이드-워셜</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="자료구조와-코드-라인별-분석">자료구조와 코드 라인별 분석</h2>

<h3 id="어떤-자료구조에-담나">어떤 자료구조에 담나?</h3>

<p>벨만-포드는 <strong>간선 중심</strong> 알고리즘이다. 다익스트라처럼 인접 리스트(노드 기준)가 아니라, <strong>간선 리스트(Edge list)</strong> 로 저장해야 한다.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="k">struct</span> <span class="nc">Edge</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">cost</span><span class="p">;</span> <span class="p">};</span>  <span class="c1">// 출발 → 도착, 가중치</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="n">Edge</span><span class="o">&gt;</span> <span class="n">edges</span><span class="p">;</span>               <span class="c1">// 모든 간선을 평탄하게 저장</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>인접 리스트(<code class="language-plaintext highlighter-rouge">vector&lt;vector&lt;pair&lt;int,int&gt;&gt;&gt;</code>)로도 구현할 수 있지만, 모든 간선을 순회할 때 중첩 루프가 필요해서 코드가 지저분해진다. 간선 리스트가 훨씬 깔끔하다.</p>

<p><code class="language-plaintext highlighter-rouge">dist</code> 배열은 1차원 <code class="language-plaintext highlighter-rouge">vector&lt;int&gt;</code> 또는 <code class="language-plaintext highlighter-rouge">vector&lt;long long&gt;</code>.</p>

<hr />

<h3 id="핵심-패턴-라인별-분석">핵심 패턴 라인별 분석</h3>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// ① dist 초기화</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dist</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>  <span class="c1">// [웜홀형] 시작점 불명 → 전부 0</span>
<span class="c1">// vector&lt;int&gt; dist(n+1, INF); dist[start] = 0;  // [일반형] 단일 출발점</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><strong>0 초기화</strong>: 모든 정점을 동시에 출발점으로 간주. “어느 정점에서 출발해도” 음수 사이클이 있으면 잡힌다.</li>
  <li><strong>INF 초기화</strong>: 특정 출발점에서의 최단거리를 구할 때. 방문 안 된 노드는 갱신 대상에서 제외(<code class="language-plaintext highlighter-rouge">dist[u] != INF</code> 체크 필요).</li>
</ul>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c1">// ② 외부 루프: N번 (또는 N-1번 + 1번 추가 검사)</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>N-1번이면 최단경로 확정, N번째는 사이클 감지용.</li>
  <li><code class="language-plaintext highlighter-rouge">i &lt; n</code>으로 N번 돌면서 <code class="language-plaintext highlighter-rouge">i == n-1</code>일 때 갱신되면 사이클 존재. 깔끔한 패턴.</li>
</ul>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c1">// ③ 내부 루프: 모든 간선 순회</span>
<span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">e</span> <span class="o">:</span> <span class="n">edges</span><span class="p">)</span> <span class="p">{</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>다익스트라와의 핵심 차이. 다익스트라는 연결된 간선만 보지만, 벨만-포드는 <strong>매 라운드 전체 간선</strong>을 본다.</li>
  <li>덕분에 음수 간선이 있어도 틀리지 않는다. 어떤 순서로 처리해도 N-1번 후엔 수렴이 보장된다.</li>
</ul>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// ④ Relaxation (완화)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="n">cost</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>핵심 연산. “u를 거쳐 v로 가는 게 현재 v까지의 비용보다 싸면 갱신”.</li>
  <li>INF 초기화 버전에서는 앞에 <code class="language-plaintext highlighter-rouge">dist[e.u] != INF &amp;&amp;</code> 조건 추가 필수. 안 달면 <code class="language-plaintext highlighter-rouge">INF + cost</code>가 오버플로우.</li>
</ul>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c1">// ⑤ 음수 사이클 감지</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>N-1번으로 모든 경로가 확정됐는데도 N번째 라운드에서 갱신이 일어남 = 무한히 줄어드는 사이클 존재.</li>
</ul>

<hr />

<h3 id="변형-포인트">변형 포인트</h3>

<p><strong>변형 1 — 출발점 있음 (일반 최단거리)</strong></p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">dist</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">INF</span><span class="p">);</span>
<span class="n">dist</span><span class="p">[</span><span class="n">start</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// 내부: if (dist[e.u] != INF &amp;&amp; dist[e.v] &gt; dist[e.u] + e.cost)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>변형 2 — 음수 사이클 노드에서 도달 가능한 모든 노드 처리</strong></p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c1">// N번째 라운드에서 갱신된 노드를 BFS/DFS로 펼쳐서</span>
<span class="c1">// 도달 가능한 모든 노드를 -INF로 마킹</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>변형 3 — SPFA (큐 최적화)</strong></p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">queue</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">q</span><span class="p">;</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span> <span class="n">inQueue</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
<span class="c1">// 갱신된 노드만 큐에 넣어서 다음 라운드에 처리</span>
<span class="c1">// 평균 O(E), 최악 O(VE)로 같지만 보통 훨씬 빠름</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>변형 4 — 최장 경로 (부호 반전)</strong></p>

<ul>
  <li>모든 간선 cost를 <code class="language-plaintext highlighter-rouge">-cost</code>로 바꾸고 최단경로 → 최장경로 변환</li>
  <li>단, 양수 사이클(원래 음수 사이클)이 생기면 무한대 주의</li>
</ul>

<hr />

<h2 id="추가로-알면-좋은-것">추가로 알면 좋은 것</h2>

<ul>
  <li><strong>SPFA (Shortest Path Faster Algorithm)</strong>: 벨만-포드를 큐로 최적화한 버전. 평균적으로 더 빠르지만 최악은 동일. 음수 사이클 감지에도 사용 가능.</li>
  <li><strong>플로이드-워셜</strong>: 모든 쌍 최단경로 필요 시 O(V³). N이 작을 때(≤500)는 웜홀처럼 플로이드도 고려 가능.</li>
  <li><strong>알고리즘 선택 기준</strong>: 음수 간선 없으면 다익스트라(O(ElogV)), 음수 간선 있거나 사이클 감지 필요하면 벨만-포드(O(VE)).</li>
</ul>

<p><strong>연관 문제 추천</strong></p>

<ul>
  <li>BOJ 11657 스타보 지하철 — 기본 벨만-포드 + 음수 사이클로 도달 불가 정점 처리</li>
  <li>BOJ 1219 오민식의 고민 — 음수 사이클 노드에서 도달 가능한 모든 노드 처리</li>
</ul>]]></content><author><name></name></author><category term="algorithm" /><category term="PS" /><summary type="html"><![CDATA[벨만-포드 알고리즘 설명 및 적용법 탐구]]></summary></entry><entry><title type="html">Jekyll 블로그 직접 만들고 노션으로 글 쓰기</title><link href="https://hadol2.github.io/posts/jekyll-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%A7%81%EC%A0%91-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C-%EA%B8%80-%EC%93%B0%EA%B8%B0/" rel="alternate" type="text/html" title="Jekyll 블로그 직접 만들고 노션으로 글 쓰기" /><published>2026-04-15T00:00:00+09:00</published><updated>2026-04-15T00:00:00+09:00</updated><id>https://hadol2.github.io/posts/jekyll-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%A7%81%EC%A0%91-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C-%EA%B8%80-%EC%93%B0%EA%B8%B0</id><content type="html" xml:base="https://hadol2.github.io/posts/jekyll-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%A7%81%EC%A0%91-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C-%EA%B8%80-%EC%93%B0%EA%B8%B0/"><![CDATA[<h2 id="왜-직접-만들었나">왜 직접 만들었나</h2>

<p>나만의 블로그를 가지고 싶었다!</p>

<p>처음엔 군대에서 Chirpy 테마를 그대로 사용해 만들다 Bootstrap 설정이 제대로 안돼서 일주일을 고생하다 유기했다. 그러다 다시 멈춰버린 시계를 작동시키기로 하고 개발을 해봤는데 <del>시험기간엔 공부빼고 다 재밌는듯 역시 포폴 제출도 할겸..</del> 테마가 기능도 많고 완성도도 높았지만, 내가 원하는 디자인과 구조로 바꾸려 할수록  복잡한 구조가 발목을 잡기도 했고, 카피라이트를 붙이는게 싫었다. 그리하여 결국 테마를 통째로 걷어내고 처음부터 직접 만들기로 했다.</p>

<hr />

<h2 id="기술-스택">기술 스택</h2>

<table>
  <thead>
    <tr>
      <th>역할</th>
      <th>도구</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>정적 사이트 생성</td>
      <td>Jekyll 4.3</td>
    </tr>
    <tr>
      <td>호스팅 / 배포</td>
      <td>GitHub Pages + GitHub Actions</td>
    </tr>
    <tr>
      <td>스타일링</td>
      <td>순수 SCSS (Bootstrap 없음)</td>
    </tr>
    <tr>
      <td>글 작성</td>
      <td>Notion</td>
    </tr>
    <tr>
      <td>발행 자동화</td>
      <td>Node.js + Notion API</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="구조-잡기">구조 잡기</h2>

<h3 id="레이아웃">레이아웃</h3>

<p>Jekyll의 레이아웃 시스템을 활용해 9개 레이아웃을 직접 작성했다.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="nx">_layouts</span><span class="o">/</span>
<span class="err">├──</span> <span class="k">default</span><span class="p">.</span><span class="nx">html</span>   <span class="err">#</span> <span class="nx">기본</span> <span class="nx">HTML</span> <span class="nx">뼈대</span>
<span class="err">├──</span> <span class="nx">home</span><span class="p">.</span><span class="nx">html</span>      <span class="err">#</span> <span class="nx">포스트</span> <span class="nx">그리드</span> <span class="o">+</span> <span class="nx">히어로</span> <span class="nx">검색</span>
<span class="err">├──</span> <span class="nx">post</span><span class="p">.</span><span class="nx">html</span>      <span class="err">#</span> <span class="nx">본문</span> <span class="o">+</span> <span class="nx">사이드</span> <span class="nx">TOC</span>
<span class="err">├──</span> <span class="nx">page</span><span class="p">.</span><span class="nx">html</span>      <span class="err">#</span> <span class="nx">일반</span> <span class="nx">페이지</span>
<span class="err">├──</span> <span class="nx">archives</span><span class="p">.</span><span class="nx">html</span>  <span class="err">#</span> <span class="nx">연도별</span> <span class="nx">아카이브</span>
<span class="err">├──</span> <span class="nx">tags</span><span class="p">.</span><span class="nx">html</span>      <span class="err">#</span> <span class="nx">태그</span> <span class="nx">목록</span>
<span class="err">├──</span> <span class="nx">tag</span><span class="p">.</span><span class="nx">html</span>       <span class="err">#</span> <span class="nx">태그별</span> <span class="nx">포스트</span>
<span class="err">├──</span> <span class="nx">categories</span><span class="p">.</span><span class="nx">html</span>
<span class="err">└──</span> <span class="nx">category</span><span class="p">.</span><span class="nx">html</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="다크모드">다크모드</h3>

<p>JavaScript 없이 CSS 변수만으로 구현했다. <code class="language-plaintext highlighter-rouge">localStorage</code>로 설정을 저장하고, <code class="language-plaintext highlighter-rouge">html[data-mode]</code> 속성으로 테마를 전환한다.</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--bg</span><span class="p">:</span> <span class="nx">#f8f7f4</span><span class="p">;</span>
  <span class="py">--text-h</span><span class="p">:</span> <span class="nx">#1c1c1c</span><span class="p">;</span>
  <span class="c">/* ... */</span>
<span class="p">}</span>

<span class="nt">html</span><span class="o">[</span><span class="nt">data-mode</span><span class="o">=</span><span class="s2">'dark'</span><span class="o">]</span> <span class="p">{</span>
  <span class="py">--bg</span><span class="p">:</span> <span class="nx">#141414</span><span class="p">;</span>
  <span class="py">--text-h</span><span class="p">:</span> <span class="nx">#f0efec</span><span class="p">;</span>
  <span class="c">/* ... */</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>페이지 로드 시 깜빡임 방지를 위해 <code class="language-plaintext highlighter-rouge">&lt;head&gt;</code> 안에 인라인 스크립트로 <code class="language-plaintext highlighter-rouge">data-mode</code>를 미리 적용한다.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;script&gt;</span>
  <span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
    <span class="kd">var</span> <span class="nx">m</span> <span class="o">=</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nf">getItem</span><span class="p">(</span><span class="dl">'</span><span class="s1">hd-mode</span><span class="dl">'</span><span class="p">)</span> <span class="o">||</span>
            <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nf">matchMedia</span><span class="p">(</span><span class="dl">'</span><span class="s1">(prefers-color-scheme: dark)</span><span class="dl">'</span><span class="p">).</span><span class="nx">matches</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">dark</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">light</span><span class="dl">'</span><span class="p">);</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nf">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">data-mode</span><span class="dl">'</span><span class="p">,</span> <span class="nx">m</span><span class="p">);</span>
  <span class="p">})();</span>
<span class="nt">&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="검색">검색</h3>

<p>Elastic Search 같은 외부 서비스 없이, Jekyll이 빌드 시 <code class="language-plaintext highlighter-rouge">search.json</code>을 생성하고 클라이언트에서 필터링하는 방식이다.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="rouge-code"><pre><span class="o">---</span>
<span class="nx">layout</span><span class="p">:</span> <span class="nx">none</span>
<span class="o">---</span>
<span class="p">[</span>
  
  <span class="p">{</span>
    <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">벨만-포드 알고리즘이란?</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">:</span>   <span class="dl">"</span><span class="s2">/posts/%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80/</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tags</span><span class="dl">"</span><span class="p">:</span>  <span class="dl">"</span><span class="s2">PS</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">content</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">## 핵심 요약</span><span class="se">\n\n\n</span><span class="s2">음수 간선이 있는 그래프에서 최단경로를 구하거나 **음수 사이클 존재 여부**를 판단할 때 쓴다. 다익스트라가 안 되는 상황에서 꺼내는 카드.</span><span class="se">\n\n\n</span><span class="s2">---</span><span class="se">\n\n\n</span><span class="s2">## 개념 설명</span><span class="se">\n\n\n</span><span class="s2">### 벨만-포드 알고리즘</span><span class="se">\n\n\n</span><span class="s2">다익스트라는 매 단계에서 현재 가장 가까운 노드를 선택해 탐색한다. 덕분에 빠르지만, 음수 간선이 있으면 이미 </span><span class="se">\"</span><span class="s2">확정</span><span class="se">\"</span><span class="s2">된 노드가 나중에 더 짧은 경로로 도달될 수 있어서 틀린 답을 낸다.</span><span class="se">\n\n\n</span><span class="s2">벨만-포드는 반대로 매 라운드마다 **모든 간선을 전부 확인**하며 dist를 갱신한다. 느리지만(O(VE)) 음수 간선도 처리할 수 있다.</span><span class="se">\n\n\n</span><span class="s2">**작동 원리**: N개의 정점을 잊는 단순 경로에는 최대 N-1개의 간선이 존재한다. 따라서 모든 간선을 N-1번 확인하면 모든 최단경로가 확정된...</span><span class="dl">"</span>
  <span class="p">},</span>
  
  <span class="p">{</span>
    <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Jekyll 블로그 직접 만들고 노션으로 글 쓰기</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">:</span>   <span class="dl">"</span><span class="s2">/posts/jekyll-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%A7%81%EC%A0%91-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EB%85%B8%EC%85%98%EC%9C%BC%EB%A1%9C-%EA%B8%80-%EC%93%B0%EA%B8%B0/</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tags</span><span class="dl">"</span><span class="p">:</span>  <span class="dl">"</span><span class="s2">blog github</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">content</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">## 왜 직접 만들었나</span><span class="se">\n\n\n</span><span class="s2">나만의 블로그를 가지고 싶었다!</span><span class="se">\n\n\n</span><span class="s2">처음엔 군대에서 Chirpy 테마를 그대로 사용해 만들다 Bootstrap 설정이 제대로 안돼서 일주일을 고생하다 유기했다. 그러다 다시 멈춰버린 시계를 작동시키기로 하고 개발을 해봤는데 ~~시험기간엔 공부빼고 다 재밌는듯 역시 포폴 제출도 할겸..~~ 테마가 기능도 많고 완성도도 높았지만, 내가 원하는 디자인과 구조로 바꾸려 할수록  복잡한 구조가 발목을 잡기도 했고, 카피라이트를 붙이는게 싫었다. 그리하여 결국 테마를 통째로 걷어내고 처음부터 직접 만들기로 했다.</span><span class="se">\n\n\n</span><span class="s2">---</span><span class="se">\n\n\n</span><span class="s2">## 기술 스택</span><span class="se">\n\n\n</span><span class="s2">| 역할        | 도구                            |</span><span class="se">\n</span><span class="s2">| --------- | -------------------...</span><span class="dl">"</span>
  <span class="p">},</span>
  
  <span class="p">{</span>
    <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">0-1 BFS (Deque BFS) 란?</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">:</span>   <span class="dl">"</span><span class="s2">/posts/0-1-bfs-deque-bfs/</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tags</span><span class="dl">"</span><span class="p">:</span>  <span class="dl">"</span><span class="s2">PS</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">content</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">핵심 요약</span><span class="se">\n\n</span><span class="s2">간선 가중치가 0 또는 1만 존재할 때, 일반 BFS 대신 deque(양방향 큐) 를 써서 O(V+E)에 최단경로를 구하는 기법이다. 비용 0인 이동은 deque 앞에, 비용 1인 이동은 deque 뒤에 넣는 것이 전부다.</span><span class="se">\n\n\n\n</span><span class="s2">개념 설명</span><span class="se">\n\n</span><span class="s2">왜 일반 BFS가 안 되는가?</span><span class="se">\n\n</span><span class="s2">일반 BFS는 “먼저 방문 = 최단거리” 가 성립하는 이유가, 모든 간선 비용이 동일하기 때문이다. 그런데 어떤 이동은 0초, 어떤 이동은 1초라면 이 전제가 깨진다. 비용 0짜리 이동을 여러 번 해도 총 비용이 안 늘어나므로, 단순 큐로는 “현재 꺼낸 노드가 최소 비용” 임을 보장할 수 없다.</span><span class="se">\n\n</span><span class="s2">그렇다고 다익스트라를 쓰면 O(E log V)인데, 가중치가 0/1뿐이라면 훨씬 더 단순하고 빠른 방법이 있다.</span><span class="se">\n\n</span><span class="s2">0-1 BF...</span><span class="dl">"</span>
  <span class="p">},</span>
  
  <span class="p">{</span>
    <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">1st-git_log</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">:</span>   <span class="dl">"</span><span class="s2">/posts/1st/</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tags</span><span class="dl">"</span><span class="p">:</span>  <span class="dl">"</span><span class="s2">github blog</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">content</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">진짜 어지럽네</span><span class="se">\n\n</span><span class="s2">액션이 안돼</span><span class="se">\n</span><span class="dl">"</span>
  <span class="p">}</span>
  
<span class="p">]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="toc-목차">TOC (목차)</h3>

<p><a href="https://tscanlin.github.io/tocbot/">tocbot</a> 라이브러리를 CDN으로 불러와 포스트 페이지에만 적용했다. 헤딩을 자동으로 파싱해서 사이드바에 스티키로 붙어있는다.</p>

<hr />

<h2 id="노션-연동">노션 연동</h2>

<p>노션을 에디터로 쓰고 싶었다. 어디서든 접근할 수 있고, 블록 기반이라 글 구성이 편하기도 하고, markdown 언어는 좀 불편한 감이 있었는데 자동으로 md로 변환되게 스크립트를 작성하니 편하다.</p>

<h3 id="구조">구조</h3>

<p>Notion에 Posts 데이터베이스를 만들고 아래 속성을 추가했다.</p>

<table>
  <thead>
    <tr>
      <th>속성</th>
      <th>타입</th>
      <th>용도</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Title</td>
      <td>제목</td>
      <td>포스트 제목</td>
    </tr>
    <tr>
      <td>Date</td>
      <td>날짜</td>
      <td>발행일</td>
    </tr>
    <tr>
      <td>Tags</td>
      <td>멀티셀렉트</td>
      <td>태그</td>
    </tr>
    <tr>
      <td>Categories</td>
      <td>멀티셀렉트</td>
      <td>카테고리</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>텍스트</td>
      <td>카드 미리보기 요약</td>
    </tr>
    <tr>
      <td>Status</td>
      <td>셀렉트</td>
      <td>Draft / Ready / Published</td>
    </tr>
  </tbody>
</table>

<h3 id="발행-스크립트">발행 스크립트</h3>

<p><code class="language-plaintext highlighter-rouge">@notionhq/client</code>와 <code class="language-plaintext highlighter-rouge">notion-to-md</code> 패키지를 사용해 Node.js 스크립트를 짰다.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre><span class="c1">// Status가 Ready인 페이지 조회</span>
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">notion</span><span class="p">.</span><span class="nx">databases</span><span class="p">.</span><span class="nf">query</span><span class="p">({</span>
  <span class="na">database_id</span><span class="p">:</span> <span class="nx">DATABASE_ID</span><span class="p">,</span>
  <span class="na">filter</span><span class="p">:</span> <span class="p">{</span> <span class="na">property</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Status</span><span class="dl">'</span><span class="p">,</span> <span class="na">select</span><span class="p">:</span> <span class="p">{</span> <span class="na">equals</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Ready</span><span class="dl">'</span> <span class="p">}</span> <span class="p">},</span>
<span class="p">});</span>

<span class="c1">// 본문 마크다운 변환</span>
<span class="kd">const</span> <span class="nx">mdBlocks</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">n2m</span><span class="p">.</span><span class="nf">pageToMarkdown</span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">body</span> <span class="o">=</span> <span class="nx">n2m</span><span class="p">.</span><span class="nf">toMarkdownString</span><span class="p">(</span><span class="nx">mdBlocks</span><span class="p">).</span><span class="nx">parent</span><span class="p">;</span>

<span class="c1">// Jekyll front matter + 본문 저장</span>
<span class="nf">writeFileSync</span><span class="p">(</span><span class="nx">filepath</span><span class="p">,</span> <span class="nx">frontmatter</span> <span class="o">+</span> <span class="nx">body</span><span class="p">);</span>

<span class="c1">// 노션 상태 업데이트</span>
<span class="k">await</span> <span class="nx">notion</span><span class="p">.</span><span class="nx">pages</span><span class="p">.</span><span class="nf">update</span><span class="p">({</span>
  <span class="na">page_id</span><span class="p">:</span> <span class="nx">page</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span>
  <span class="na">properties</span><span class="p">:</span> <span class="p">{</span> <span class="na">Status</span><span class="p">:</span> <span class="p">{</span> <span class="na">select</span><span class="p">:</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Published</span><span class="dl">'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">},</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="워크플로우">워크플로우</h3>

<ol>
  <li>노션에서 글 작성</li>
  <li>Status → <code class="language-plaintext highlighter-rouge">Ready</code></li>
  <li><code class="language-plaintext highlighter-rouge">node scripts/publish.js</code> 실행</li>
  <li><code class="language-plaintext highlighter-rouge">_posts/</code> 저장 → 노션 <code class="language-plaintext highlighter-rouge">Published</code> → git commit + push → GitHub Actions 배포</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">publish.command</code> 파일을 만들어두면 터미널 없이 더블클릭만으로 실행할 수 있다.</p>

<hr />

<h2 id="배포">배포</h2>

<p>GitHub Actions를 사용해 <code class="language-plaintext highlighter-rouge">main</code> 브랜치에 push하면 자동으로 빌드 후 배포된다.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">ruby/setup-ruby@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">ruby-version</span><span class="pi">:</span> <span class="m">3</span>
          <span class="na">bundler-cache</span><span class="pi">:</span> <span class="kc">true</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec jekyll build</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-pages-artifact@v3</span>

  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/deploy-pages@v4</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>별도 서버 없이 GitHub이 무료로 호스팅과 배포를 모두 처리한다.</p>

<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/f8f93fa5-6198-41f7-aede-a1e57b838745/45ee65f9-df3b-41a7-aa2e-6efb88a9ad57/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&amp;X-Amz-Credential=ASIAZI2LB466QNYHHBAH%2F20260414%2Fus-west-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20260414T152730Z&amp;X-Amz-Expires=3600&amp;X-Amz-Security-Token=IQoJb3JpZ2luX2VjEMf%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLXdlc3QtMiJGMEQCICtolDvzWx3ldrBDOcj0uubCf6WX%2BM20HJhztZVps%2FTMAiA24AXFIFi8lGprU7RVqIBxiRSXF6IXri8JDI2WQVvMBSqIBAiQ%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDYzNzQyMzE4MzgwNSIMajrPCCjgMlRA9NnzKtwD2Uoa1dsZYQLd%2B%2BKyCLKzQIWFJt3qL2EBEbokbNm4pHtU7cXbr9ltl4%2B3nowlW3SNaa49GlvwL3og0MJ%2FG%2FYEAqhX%2FS1frIxWhy%2BsfQ3h%2FHWCvz%2B0Q%2By0lw54r1J1Xxx2YGtxq1B0SMf0VP%2BAdcN9aBCQjgMjFnhSdMpiGOPmtb1tZuCu8PHIFMjK%2FHbuwQbeNHCwU3floRv1L1mJg8R9fWRzPoQrGExUcyFnn%2FQJDEsc1lFiExRBuFaU2YIvtG0Aa%2FTf3FjHbiA3%2FsmW336njbim89h7NwBe8wtzLxHWs9m7%2FMoiGyI%2BpT0vc7KbAtUjdGq6eWJyjvv%2BOhA5Gzop9PA6az%2B8NFo2GqN5U4cRk8dKud6bffv5VrsjQgnig0NhpJ20AvtacmT9EoVIWLH1OQwBNPa8zrvm23dDRCVplpWV49V5mmWWjCsKo6scewTHYPbwpn9gZu46rZAUQyCYUR79yn3Ypu5FXq3G5b4zT94JFU1sPQ%2FBPWGyJ9dfkRauzebta3u0tY0n9VD0867RgVy2QTUKW%2BBlY2eIO9BWIa0WuTYFCZRIXdUDRZMvUlpaZ5RCuYNwxvC8bSzK9PC%2Bbfnm5aXKT70Gnn7DksCR86ZkTivB%2FpG8PWuF5a8wqaD5zgY6pgH3g4WlBqvMnxih5K%2BHNvFsAIhT%2F27PanD22UU9%2BZcOLNZ7ihjWrHUmc%2FGp9yqUrH%2BJrFMHdLaa4vhzbirL%2BxWI0r5hhwjbFTVH1jV04b%2BTafb7XcTNq3wOwobftd9ziqCLHsGKEgvlEd84N2tszMnGrmQKCeoCqaIhA5xpfkWJ689VPNkepbiUSXwUP2%2FCkCP1E8IQeQGTAnj0kipnQoTuorbh7Bm%2F&amp;X-Amz-Signature=7e21fcff2e39b1f749236c47697e53d0d89854eeb53e401f9f94d2879354cd93&amp;X-Amz-SignedHeaders=host&amp;x-amz-checksum-mode=ENABLED&amp;x-id=GetObject" alt="image.png" /></p>

<p>Deploy에 성공하게 되면 이런 배포된 사이트의 주소를 알려주는데 클릭하면 비로소 내 블로그가 나의 서버 위에서가 아닌 세상에 공개된다.</p>

<p>다른 것 보다 이런 저런 오류로 Deploy에서 가장 시간을 많이 잡아먹으니 마음의 준비를 하길..</p>

<hr />

<h2 id="마치며">마치며</h2>

<p>처음부터 직접 만드는 게 귀찮아 보일 수 있지만, 결과적으로 구조를 완전히 이해하고 원하는 대로 바꿀 수 있다는 게 가장 큰 장점이었다. 특히 노션 연동으로 글 쓰는 과정이 훨씬 편해졌다.</p>

<p>전체 소스는 <a href="https://github.com/jhs9918/hadol.github.io">GitHub</a>에서 볼 수 있다.</p>]]></content><author><name></name></author><category term="devlog" /><category term="blog" /><category term="github" /><summary type="html"><![CDATA[Chirpy 테마를 걷어내고 Jekyll 블로그를 직접 만든 과정, 그리고 노션을 에디터로 연결하기까지]]></summary></entry><entry><title type="html">0-1 BFS (Deque BFS) 란?</title><link href="https://hadol2.github.io/posts/0-1-bfs-deque-bfs/" rel="alternate" type="text/html" title="0-1 BFS (Deque BFS) 란?" /><published>2026-04-14T00:00:00+09:00</published><updated>2026-04-14T00:00:00+09:00</updated><id>https://hadol2.github.io/posts/0-1-bfs-deque-bfs</id><content type="html" xml:base="https://hadol2.github.io/posts/0-1-bfs-deque-bfs/"><![CDATA[<h2 id="핵심-요약">핵심 요약</h2>

<p>간선 가중치가 <strong>0 또는 1</strong>만 존재할 때, 일반 BFS 대신 <strong>deque(양방향 큐)</strong> 를 써서 O(V+E)에 최단경로를 구하는 기법이다. 비용 0인 이동은 deque 앞에, 비용 1인 이동은 deque 뒤에 넣는 것이 전부다.</p>

<hr />

<h2 id="개념-설명">개념 설명</h2>

<h3 id="왜-일반-bfs가-안-되는가">왜 일반 BFS가 안 되는가?</h3>

<p>일반 BFS는 “먼저 방문 = 최단거리” 가 성립하는 이유가, 모든 간선 비용이 동일하기 때문이다. 그런데 어떤 이동은 0초, 어떤 이동은 1초라면 이 전제가 깨진다. 비용 0짜리 이동을 여러 번 해도 총 비용이 안 늘어나므로, 단순 큐로는 “현재 꺼낸 노드가 최소 비용” 임을 보장할 수 없다.</p>

<p>그렇다고 다익스트라를 쓰면 O(E log V)인데, 가중치가 0/1뿐이라면 훨씬 더 단순하고 빠른 방법이 있다.</p>

<h3 id="0-1-bfs의-아이디어">0-1 BFS의 아이디어</h3>

<p>핵심은 <strong>“비용 0짜리 이동은 현재 레벨에서 처리한다”</strong> 는 것이다.</p>

<ul>
  <li>비용 <strong>0</strong> 인 간선 → 총 비용 그대로이므로 <strong>deque 앞</strong>(<code class="language-plaintext highlighter-rouge">push_front</code>)에 삽입</li>
  <li>비용 <strong>1</strong> 인 간선 → 총 비용이 1 증가하므로 <strong>deque 뒤</strong>(<code class="language-plaintext highlighter-rouge">push_back</code>)에 삽입</li>
</ul>

<p>이렇게 하면 deque에서 꺼내는 노드는 항상 현재까지의 최소 비용 순서가 보장된다. 다익스트라의 우선순위 큐를 O(1) 삽입 deque으로 대체한 셈이다.</p>

<p><strong>언제 쓰나?</strong></p>
<ul>
  <li>그래프 간선 가중치가 오직 0과 1만 존재할 때</li>
  <li>상태공간 탐색에서 어떤 행동은 무료(0), 어떤 행동은 비용 1일 때</li>
  <li>BFS보다 유연하고 다익스트라보다 빠른 풀이가 필요할 때</li>
</ul>

<p><strong>시간복잡도</strong>: O(V + E) — 다익스트라의 O(E log V)보다 빠르다</p>

<p><strong>핵심 패턴 (C++)</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">MAX</span> <span class="o">=</span> <span class="mi">100001</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">dist</span><span class="p">[</span><span class="n">MAX</span><span class="p">];</span>

<span class="kt">void</span> <span class="nf">bfs01</span><span class="p">(</span><span class="kt">int</span> <span class="n">start</span><span class="p">,</span> <span class="kt">int</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fill</span><span class="p">(</span><span class="n">dist</span><span class="p">,</span> <span class="n">dist</span> <span class="o">+</span> <span class="n">MAX</span><span class="p">,</span> <span class="n">INT_MAX</span><span class="p">);</span>
    <span class="n">deque</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dq</span><span class="p">;</span>

    <span class="n">dist</span><span class="p">[</span><span class="n">start</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">start</span><span class="p">);</span>

    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">dq</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">();</span>
        <span class="n">dq</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>

        <span class="c1">// 비용 0인 이동 → deque 앞에</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">&lt;</span> <span class="n">MAX</span> <span class="o">&amp;&amp;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">])</span> <span class="p">{</span>
            <span class="n">dist</span><span class="p">[</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">];</span>
            <span class="n">dq</span><span class="p">.</span><span class="n">push_front</span><span class="p">(</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="c1">// 비용 1인 이동 → deque 뒤에</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">next</span> <span class="o">:</span> <span class="p">{</span><span class="n">cur</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur</span> <span class="o">+</span> <span class="mi">1</span><span class="p">})</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">next</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">next</span> <span class="o">&lt;</span> <span class="n">MAX</span> <span class="o">&amp;&amp;</span> <span class="n">dist</span><span class="p">[</span><span class="n">next</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">dist</span><span class="p">[</span><span class="n">next</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
                <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">next</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">end</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<hr />

<h2 id="문제에서의-적용">문제에서의 적용</h2>

<p><strong>BOJ 13549</strong></p>

<p>수빈이는 위치 N에서 출발해 동생이 있는 위치 K에 도달해야 한다. 이동 방법은 세 가지다.</p>

<ul>
  <li>X → X-1 또는 X+1 : <strong>1초</strong> 소요</li>
  <li>X → 2X 순간이동 : <strong>0초</strong> 소요</li>
</ul>

<p>가중치가 딱 0과 1이므로, 0-1 BFS가 최적이다. 순간이동(×2)은 비용 0이니 deque 앞, 걷기(±1)는 비용 1이니 deque 뒤에 넣으면 된다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="rouge-code"><pre><span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">dq</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">();</span>
    <span class="n">dq</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">cur</span> <span class="o">==</span> <span class="n">K</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">K</span><span class="p">];</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// 순간이동: 0초 → 앞에</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">&lt;=</span> <span class="mi">100000</span> <span class="o">&amp;&amp;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">])</span> <span class="p">{</span>
        <span class="n">dist</span><span class="p">[</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">];</span>
        <span class="n">dq</span><span class="p">.</span><span class="n">push_front</span><span class="p">(</span><span class="n">cur</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// 걷기: 1초 → 뒤에</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">nx</span> <span class="o">:</span> <span class="p">{</span><span class="n">cur</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">cur</span> <span class="o">+</span> <span class="mi">1</span><span class="p">})</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">nx</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">nx</span> <span class="o">&lt;=</span> <span class="mi">100000</span> <span class="o">&amp;&amp;</span> <span class="n">dist</span><span class="p">[</span><span class="n">nx</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">dist</span><span class="p">[</span><span class="n">nx</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">cur</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">nx</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>포인트</strong>: N &gt;= K이면 순간이동이 무의미하므로 단순히 N - K가 답이다.</p>

<hr />

<h2 id="주의할-점--흔한-실수">주의할 점 / 흔한 실수</h2>

<ul>
  <li><strong>실수 1: 범위 초과</strong> — <code class="language-plaintext highlighter-rouge">cur * 2</code>가 100000을 넘을 수 있으니 반드시 범위 체크</li>
  <li><strong>실수 2: dist 초기화 안 함</strong> — INT_MAX로 초기화하지 않으면 오답 또는 무한루프</li>
  <li><strong>실수 3: 음수 위치</strong> — <code class="language-plaintext highlighter-rouge">cur - 1</code>이 0 미만이 될 수 있으니 <code class="language-plaintext highlighter-rouge">nx &gt;= 0</code> 조건 필수</li>
  <li><strong>실수 4: 일반 BFS로 풀기</strong> — 비용이 0/1 혼재인데 queue만 쓰면 최단경로 보장 안 됨</li>
  <li><strong>실수 5: cur * 2 오버플로우</strong> — 이 문제는 최대 100000이라 괜찮으나 일반적으론 주의</li>
</ul>

<hr />

<h2 id="추가로-알면-좋은-것">추가로 알면 좋은 것</h2>

<p><strong>다익스트라와의 관계</strong>: 0-1 BFS는 가중치가 0/1로 제한된 다익스트라의 특수 케이스다. 가중치 범위가 0~K로 늘어나면 Dial’s Algorithm(버킷 큐)을 쓰고, 일반적인 양수 가중치면 다익스트라를 쓴다.</p>

<p><strong>변형 문제 유형</strong>:</p>
<ul>
  <li>격자에서 어떤 칸은 무료 통과, 어떤 칸은 비용 1 → 0-1 BFS</li>
  <li>문 열기(비용 1) vs 그냥 통과(비용 0) 유형</li>
</ul>

<p><strong>연습 추천 문제</strong>:</p>
<ul>
  <li>BOJ 1261 (알고스팟) — 0-1 BFS 교과서 문제</li>
  <li>BOJ 13913 (숨바꼭질 4) — 경로 복원 추가</li>
</ul>]]></content><author><name></name></author><category term="algorithm" /><category term="PS" /><summary type="html"><![CDATA[Deque BFS 설명 및 관련 문제 풀이]]></summary></entry><entry><title type="html">1st-git_log</title><link href="https://hadol2.github.io/posts/1st/" rel="alternate" type="text/html" title="1st-git_log" /><published>2024-07-08T23:40:00+09:00</published><updated>2025-02-26T00:12:12+09:00</updated><id>https://hadol2.github.io/posts/1st</id><content type="html" xml:base="https://hadol2.github.io/posts/1st/"><![CDATA[<h1 id="진짜-어지럽네">진짜 어지럽네</h1>

<p>액션이 안돼</p>]]></content><author><name></name></author><category term="programing" /><category term="github" /><category term="blog" /><summary type="html"><![CDATA[진짜 어지럽네]]></summary></entry></feed>