<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_noob8933.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 21 Apr 2026 03:24:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_noob8933.log</title>
            <url>https://velog.velcdn.com/images/dev_noob8933/profile/fdee0b82-2d2d-43aa-a994-0f279ee44cc7/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_noob8933.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_noob8933" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[* vs & vs &]]></title>
            <link>https://velog.io/@dev_noob8933/tel7eoax</link>
            <guid>https://velog.io/@dev_noob8933/tel7eoax</guid>
            <pubDate>Tue, 21 Apr 2026 03:24:25 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th>상황</th>
<th>적절한 방법</th>
</tr>
</thead>
<tbody><tr>
<td>하나의 객체 공유 (grid 등)</td>
<td><code>&amp;</code> (reference)</td>
</tr>
<tr>
<td>객체들 간 연결 관계 (그래프, 트리)</td>
<td><code>*</code> (pointer)</td>
</tr>
</tbody></table>
<hr>
<p>내가 헷갈렸던 지점은</p>
<p><code>분명 DFS 문제 풀이때는 2차원 벡터를 매개변수로 받았을 때, 직접 값을 변경해주기 위해 &amp;를 사용했었는데, 왜 BFS에서는 큐의 원소로 vertex 구조체를 넣으면서 관리하려니 안되는걸까?</code></p>
<p>우선 DFS에서 <code>&amp;</code>가 잘 동작했던 이유는</p>
<ul>
<li><code>void dfs(vector&lt;vector&lt;int&gt;&gt;&amp; grid</code><ul>
<li><code>grid</code>는 하나의 객체</li>
<li>함수 안에서도 그 동일 객체를 계속 사용</li>
<li><blockquote>
<p>즉, 참조 하나로 충분</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h3 id="reference">&amp; (Reference)</h3>
<ul>
<li>Vertex&amp; a = b;<blockquote>
<ul>
<li><code>a</code>는 b의 별명이다.</li>
<li><strong>한 번 연결되면 다른 객체로 바꿀 수 없다.</strong>
즉, <code>이 정점의 parent를 나중에 정점으로 바꾼다</code>거나, <code>여러 정점을 컨테이너에 넣고 연결한다.</code> 와 같은 동작이 불가능하거나 매우 제한되는 것.</li>
</ul>
</blockquote>
</li>
</ul>
<p>단순히 &#39;값 복사를 피하면서 직접 수정이 가능하다&#39;가 아니라, 한 번 연결된 경우 수정이 불가한 비가역적인 기능이었다.</p>
<h3 id="-pointer">* (Pointer)</h3>
<pre><code>Vertex* p = &amp;a;
p = &amp;b;</code></pre><blockquote>
<ul>
<li>포인터는 주소를 저장하는 변수이기 때문에 a의 주소를 저장해두었다.</li>
<li>포인터 p의 경우 저장중인 주소값을 다른 값으로 변경할 수 있다.</li>
<li>따라서 여러 객체를 자유롭게 연결할 수 있는 것이다.</li>
<li>여기서 쓰인 <code>&amp;</code>는 참조가 아니라, 주소를 가져오는 연산자이다.</li>
</ul>
</blockquote>
<hr>
<p>정리하자면, 핵심 원인은 <strong>값 복사 vs 동일 객체 참조</strong>의 차이였고, 나는 그 기능을 잘 이해하지 못한 채 참조 기능을 오용 및 남용하고 있었다.</p>
<h3 id="최종-정리">최종 정리</h3>
<p>참조(<code>&amp;</code>)는 복사를 막고, 같은 객체를 쓰게 해주는 도구.
포인터(<code>*</code>)는 객체들 간의 연결 구조를 만들 수 있는 도구.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DP DP DP DP DP DP]]></title>
            <link>https://velog.io/@dev_noob8933/DP-DP-DP-DP-DP-DP</link>
            <guid>https://velog.io/@dev_noob8933/DP-DP-DP-DP-DP-DP</guid>
            <pubDate>Sat, 18 Apr 2026 08:08:17 GMT</pubDate>
            <description><![CDATA[<p>Dynamic programming is applicable when the subproblems are independent!!</p>
<ul>
<li>Characterize the sutructure of an optimal solution</li>
<li>Recursively define the vaule of an optimal solution</li>
<li>Compute the value of an optimal solution in a bottom-up fashion</li>
<li>Construct an optimal solution from computed information</li>
</ul>
<hr>
<p>In order for dynamic programming to apply, the problem must have</p>
<ol>
<li><p>Optimal substructure</p>
<blockquote>
<ul>
<li>전체 최적해는 부분문제들의 최적해들로 이루어진다.</li>
<li>어떤 문제가 optimal substructure를 가지면, 그 문제는 동적 계획법을 적용할 수 있는 강한 신호이다.</li>
<li>동적 계획법에서는 부분문제들의 최적해를 이용하여 전체 문제의 최적해를 구성한다.</li>
<li>결과적으로, 우리가 고려하는 부분문제의 범위가 최적해를 구성하는 데 사용되는 부분문제들을 포함하도록 보장해야 한다.</li>
</ul>
</blockquote>
<ul>
<li>최적해를 구성하는 데 필요한 ‘모든 종류의 부분문제 상태’가 정의 안에 포함되어야 한다.</li>
</ul>
</li>
<li><p>Overlapping subproblems</p>
<blockquote>
<ul>
<li>When a recursive algorithm revisits the same problem repeatedly, we say that the optimization problem has &quot;overlapping subproblems&quot;.</li>
</ul>
</blockquote>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Quick sorting 2]]></title>
            <link>https://velog.io/@dev_noob8933/Quick-sorting-2</link>
            <guid>https://velog.io/@dev_noob8933/Quick-sorting-2</guid>
            <pubDate>Thu, 16 Apr 2026 06:18:19 GMT</pubDate>
            <description><![CDATA[<h2 id="analysis-of-quicksort">Analysis of Quicksort</h2>
<p>재귀식은 아래와 같다.</p>
<pre><code>T(n) = T(i) + T(n-i-1) + n

// 1, 2, ..., i, i+1(pivot), i+2, ..., n-1, n
// 즉, 좌집합 T(i), 우집합 (n-i-1)에 대한 Conquer 진행.
// 뒤의 n은 combine과정이 아니라 Partition cost를 의미한다.</code></pre><p>따라서, Worst case는 피봇 기준 어느 한 쪽으로 계속 치우쳐지는 경우로,</p>
<pre><code>T(n) = T(n-1) + n
     = T(n-2) + (n-1) + n
     = T(n-3) + (n-2) + (n-1) + n
     ...
     = T(1) + 2 + 3 + ... + (n-1) + n
     = T(1) + (n+2)*(n-1)/2
     = O(n^2)</code></pre><p>반면, Best case는 피봇 기준 항상 n/2로 나눠지는 경우로,</p>
<pre><code>T(n) = 2*T(n/2) + n

Master method에 의해 n^log2(2) = n이 f(n) = n과 같으므로
Theta(nlog(n)).</code></pre><hr>
<p>정리하기보단 직접 풀면서 공부하는 게 훨씬 효율이 좋은 듯 하여 여기까지만 기록하기로.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Quick Sorting 1]]></title>
            <link>https://velog.io/@dev_noob8933/Quick-Sorting-1</link>
            <guid>https://velog.io/@dev_noob8933/Quick-Sorting-1</guid>
            <pubDate>Wed, 15 Apr 2026 12:47:05 GMT</pubDate>
            <description><![CDATA[<ul>
<li>퀵소트의 코드를 보면<pre><code>template &lt;class T&gt;
void QuickSort(T *a, const int left, const int right)
{ 
// Sort a[left:right] into nondecreasing order.
// a[left] is arbitrarily chosen as the pivot. Variables i and j
// are used to partition the subarray so that at any time a[m] ≤ pivot, m &lt; i
// and a[m] ≥ pivot, m &gt; j. It is assumed that a[left] ≤ a[right + 1]
</code></pre></li>
</ul>
<p>if (left &lt; right) {
    int i=left,
    j = right + 1,
    pivot = a[left];
    do {
        do i++; while (a[i] &lt; pivot);
        do j--; while (a[j] &gt; pivot);
        if(i &lt; j) swap(a[i], a[j]);
    } while (i&lt;j);</p>
<pre><code>swap(a[left], a[j]);
QuickSort(a, left, j-1);
QuickSort(a, j+1, right);
}</code></pre><p>}</p>
<p>```</p>
<ul>
<li>재귀 호출이 partition 작업이 끝난 후에야 수행되는 것을 볼 수 있다.</li>
<li>partition이 In-place 방식이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Heapsort 2]]></title>
            <link>https://velog.io/@dev_noob8933/Heapsort-2</link>
            <guid>https://velog.io/@dev_noob8933/Heapsort-2</guid>
            <pubDate>Wed, 15 Apr 2026 12:16:35 GMT</pubDate>
            <description><![CDATA[<h2 id="build-max-heap-correction-proof">Build max heap correction proof</h2>
<h3 id="loop-invariant">Loop Invariant</h3>
<p>복습)
<strong>Initialization</strong> : It is true prior to the first iteration of the loop.
<strong>Maintenance</strong> : If it is true before an iteration of the loop, it remains true before the next iteration.
<strong>Termination</strong> : When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.</p>
<hr>
<p>Build-Max-Heap의 Loop Invariant</p>
<blockquote>
<p>At the start of each iteration of the for loop, each node i+1, i+2, ..., n-1, n is the root of a max-heap.</p>
</blockquote>
<p><strong>Initialization</strong></p>
<ul>
<li>At the start of the for loop, i = floor(A.length/2). Each node i+1, i+2 ... n은 리프노드이므로 자명한 max heap이다.</li>
<li><blockquote>
<p>L.I hold</p>
</blockquote>
</li>
</ul>
<p><strong>Maintenance</strong></p>
<ul>
<li>L.I에 의해 i의 두 자식들은 max heap이다. max-heapify(A, i)는 i를 루트 노드로 하는 서브트리를 max-heap으로 만들어주며, i+1, i+2, ..., n을 각각 루트노드로 하는 서브트리에 대해 max heap 성질을 유지한다.</li>
<li>i를 감소시키면 다음 수행을 위한 L.I가 여전히 만족한다. &quot;i, i+1, i+2, ..., n-1, n에 대해 ~&quot;</li>
</ul>
<p><strong>Termination</strong></p>
<ul>
<li>i가 0일 때 종료. 이를 L.I에 넣어보면,</li>
<li>Each node 1, 2, ..., n is the root of a max-heap이 되며, 이는 node 1을 루트로 하는 서브트리, 즉 전체 트리가 max-heap이라는 뜻이다.</li>
<li><blockquote>
<p>그러므로 Build-max-heap은 max heap을 만드는 데에 correct algorithm.</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="simpler-upper-bound">Simpler Upper Bound</h2>
<ul>
<li>Build-max-heap 자체가 i = floor(n/2)부터 1까지 순회하므로 O(n), 각 순회마다 O(logn)이 걸리는 Max-Heapify를 콜한다.</li>
</ul>
<h2 id="tighter-upper-bound">Tighter Upper Bound</h2>
<ul>
<li>Max-Heapify는 트리 높이가 h일 때 O(h)의 시간이 걸린다.</li>
<li>높이 h는 전체 노드 n개일 때 floor(log(n))이다.<blockquote>
</blockquote>
height : 그 노드에서 아래로 내려가 리프에 도달하는 가장 긴 경로의 간선 수
depth : 루트에서부터의 거라. level이라고도 함.
<img src="https://velog.velcdn.com/images/dev_noob8933/post/4f309921-3141-4dc7-bf1b-22f351a85ac1/image.png" alt=""></li>
<li><blockquote>
<p>중간에 +1을 넣어서 ceil fn을 벗겨내는 디테일 좋다.</p>
</blockquote>
</li>
</ul>
<p>따라서, Build max heap의 upper bound를 더욱 타이트하게 잡아보면
O(nlog(n))에서 O(n)까지 만들 수 있다.</p>
<h2 id="another-analysis-of-building-a-heap">Another Analysis of Building a Heap</h2>
<blockquote>
<p>Let&#39;s assume the tree is <strong>Complete binary tree</strong>.
그러면 노드의 총 개수 n은 2^h - 1개가 된다.</p>
</blockquote>
<ul>
<li>높이가 h인 노드의 개수는 1개이며, 이 노드에서 max heapify는 최악의 경우 h만큼 내려간다.</li>
<li>높이가 h-1인 노드의 개수는 2개이며, 이 노드에서는 h-1만큼 내려간다.
...</li>
<li>이를 식으로 표현해보면
cost S = h + 2<em>(h-1) + 2^2</em>(h-2) + 2^3<em>(h-3) + ... + 2^(h-1)\</em>(1) + 2^h*0
2S = 2h + 2^2(h-1) + 2^3(h-2) + ... + 2^h(1)
...
S = 2*2^(log(n)) - log(n) - 2 &lt;= 2n</li>
</ul>
<h2 id="heapsort">Heapsort</h2>
<ol>
<li>Build max heap을 통해 max-heap으로 만든다.</li>
<li>최댓값이 항상 root에 있으므로 맨 마지막 원소인 A[n]과 A[1]을 교환한다.</li>
<li>Max-heapify(A,1)을 A[1...n-1]까지 수행한다.</li>
<li>heap의 사이즈가 2가 될 때까지 1~3 과정 반복.
 -&gt; 어차피 A[1]과 A[2]는 이미 정렬되어있기 때문.</li>
</ol>
<h3 id="running-time">Running time</h3>
<ul>
<li>Build max heap : O(n)</li>
<li>Each of the n-1 calls to Max-Heapify takes O(lgn) time.</li>
<li><blockquote>
<p>O(nlgn)</p>
</blockquote>
</li>
</ul>
<h2 id="priority-queue">Priority Queue</h2>
<pre><code>MAX_HEAP_INCREASE_KEY(A, i, key)
if (key &lt; A[i])
    return &quot;new key is smaller than current key&quot;

A[i] = key
while i &gt; 1 &amp;&amp; A[parent(i)] &lt; A[i]
    exchange(A[parent(i)], A[i])
    i = parent(i)

MAX_HEAP_INSERT(A, key)
A.heap-size = A.heap-size + 1
A[heap-size] = -Inf
MAX_HEAP_INCREASE_KEY(A, A.heap-size, key)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Heapsort 1]]></title>
            <link>https://velog.io/@dev_noob8933/Heap-Sort-1</link>
            <guid>https://velog.io/@dev_noob8933/Heap-Sort-1</guid>
            <pubDate>Wed, 15 Apr 2026 08:27:43 GMT</pubDate>
            <description><![CDATA[<h2 id="comparation">Comparation</h2>
<p>vs Insertion sort,</p>
<ol>
<li>in-place.</li>
<li>running time is O(nlgn).</li>
</ol>
<p>vs Merge sort,</p>
<ol>
<li>running time is same as O(nlgn).</li>
<li>it does not require O(n) additional space.</li>
</ol>
<p>-&gt; Heap sort를 선택하는 대표 조건은 &quot;추가 메모리를 거의 못 쓰면서도, 항상 O(nlgn) 성능을 보장해야 할 때&quot; 이다.</p>
<h2 id="heap">Heap</h2>
<p>배열 A에서</p>
<ul>
<li>A.length : the number of elements in the array</li>
<li>A.heap-size : the number of elements in the heap</li>
<li>only the elements in A[1...A.heap-size], where 0 &lt;= A.heap-size &lt;= A.length are valid elements of the heap.</li>
<li>A[1] is the root of the tree</li>
<li>index i가 있을 때<ul>
<li>2i : left child (if 2i &lt;= A.heap-size)</li>
<li>2i + 1 : right child (if 2i + 1 &lt;= A.heap-size)</li>
<li>parent : floor(i/2)</li>
</ul>
</li>
<li>The height of a node is the number of edges on the longest simple downward path from the node to a leaf.</li>
<li>Since a heap of n elements is based on a complete binary tree, its height is O(lgn).</li>
<li>The height of the heap is the height of the root.</li>
</ul>
<h2 id="max-heapify">Max Heapify</h2>
<h3 id="시간복잡도-분석">시간복잡도 분석</h3>
<ul>
<li>어떤 노드 i를 루트로 하는 서브트리의 크기가 n이라고 하자.</li>
<li>즉, i 아래에 총 n개의 노드가 있다고 하자.</li>
<li><blockquote>
<p>그럼 이 노드의 자식 중 하나가 가질 수 있는 최대 서브트리 크기는 얼마인가?</p>
</blockquote>
</li>
</ul>
<blockquote>
</blockquote>
<p>왜 subtree의 사이즈는 최대 2n/3을 못넘기는가?</p>
<pre><code>높이가 0 ~ H-1인 완전이진트리가 있다고 하자.
전체 노드 수는 2^H - 1개.
&gt;
높이가 1인 두 노드중 좌측 노드를 루트 노드로 하는 서브 트리를 LST라 하자. 
LST의 크기는 2^(H-1) - 1개.
&gt;
LST에만 리프노드를 전부 채운다고 하면, 이 때 리프노드의 개수는 2^(H-1)개. 
전체 노드 개수와 LST에 리프노드 개수를 각각 더해주면
전체 노드 개수는 2^H - 1 + 2^(H-1) = 3/2 * 2^H - 1
&gt;
LST의 개수는 2^H - 1
&gt;
따라서
LST/전체노드개수 = 2^H - 1 / (3/2 * 2^H - 1) = 약 2/3</code></pre><p>그래서 아래 문장도 설명된다.</p>
<ul>
<li><p>For a subtree of size n rooted at a given node i, the size of the subree rooted at a child of node i becomes maximum when the bottom level of the tree is exactly half full.</p>
</li>
<li><p>노드 i를 루트로 하는 사이즈 n짜리 서브트리에서의 수행 시간은 다음으로 이루어져 있다.</p>
</li>
</ul>
<ol>
<li>A[i], A[left], A[right] 정렬 - Theta(1).</li>
<li>T(2n/3) to recursively process on a subtree rooted at one of the children of node i.</li>
</ol>
<ul>
<li>이를 정리하면 T(n) &lt;= T(2n/3) + Theta(1) 인 것.
그러므로 부등식임에 따라 T(n) = O(lgn)이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Maximum Subarray problem]]></title>
            <link>https://velog.io/@dev_noob8933/Maximum-Subarray-problem</link>
            <guid>https://velog.io/@dev_noob8933/Maximum-Subarray-problem</guid>
            <pubDate>Wed, 15 Apr 2026 06:52:37 GMT</pubDate>
            <description><![CDATA[<h2 id="proof-using-cut-and-paste">Proof using &#39;cut and paste&#39;</h2>
<blockquote>
</blockquote>
<p>우리가 보이고 싶은 명제는
&quot;전체 배열의 maximum subarray는</p>
<pre><code>1. 왼쪽 부분 배열의 maximum subarray
2. 오른쪽 부분 배열의 maximum subarray
3. mid를 가로지르는 maximum subarray</code></pre><p>위 세 가지 중 하나이다.&quot;</p>
<p>전체 배열의 최적해 A[i...j]를 하나 잡았다고 하자.</p>
<ol>
<li>최적해의 원소들이 전부 mid보다 왼쪽 구간에 있으면 A[i...j]는 왼쪽 부분배열 안에서의 maximum subarray이다.<ul>
<li>만약 왼쪽 구간에 A[i...j]보다 합이 더 큰 부분배열 A[p...q]가 존재한다면</li>
<li>이는 기존 최적해 A[i...j]를 A[p...q]로 잘라 붙여 더 큰 합의 해를 만들 수 있기 때문에 A[i...j]가 최적해라는 가정에 모순이 된다.</li>
<li>따라서 A[i...j]는 왼쪽 부분배열의 maximum subarray이다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>최적해의 원소들이 전부 mid보다 오른쪽 구간에 있으면 A[i...j]는 오른쪽 부분배열 안에서의 maximum subarray이다.</p>
<ul>
<li>만약 오른쪽 구간에 A[i...j]보다 합이 더 큰 부분배열 A[p...q]가 존재한다면</li>
<li>이는 기존 최적해 A[i...j]를 A[p...q]로 잘라 붙여 더 큰 합의 해를 만들 수 있기 때문에 A[i...j]가 최적해라는 가정에 모순이 된다.</li>
</ul>
</li>
<li><p>최적해의 원소들중 mid를 지나는 원소가 있다면, A[i...j]는 crossing array중 maximum subarray이다.</p>
<ul>
<li>crossing 최적해 A[i...j]는 왼쪽은 mid에서 끝나는 최대 suffix, 오른쪽은 mid+1에서 시작하는 최대 prefix의 결합이다.</li>
<li>만약 A[i...mid]보다 합이 더 큰 suffix A[p...mid]가 존재한다면</li>
<li>A[i...j]를 A[p...j]로 교체할 수 있다.</li>
<li>이는 기존 최적해 A[i...j]보다 더 큰 합의 해를 만들 수 있기 때문에 A[i...j]가 최적해라는 가정에 모순이 된다.</li>
<li>따라서 A[i...mid]는 최대 suffix이다.</li>
<li>마찬가지로, 만약 A[mid+1...j]보다 더 큰 prefix A[mid+1...q]가 존재한다면</li>
<li>A[i...j]를 A[i...q]로 교체할 수 있으므로 가정에 모순이다.</li>
<li>따라서 A[i...mid]는 mid에서 끝나는 최대 suffix, A[mid+1...j]는 mid+1에서 시작하는 최대 prefix 형태이며, 따라서 이는 crossing subarray들중 maximum subarray이다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="msp-in-linear-time">MSP in Linear time</h2>
<pre><code>#include &lt;iostream&gt;
using namespace std;

int main() {
    int arr[11] = { 0, 2, -3, 5, -1, -2, -4, 10, 7, -2, -3};

    int thisSum = 0;
    int maxSum = 0;
    for (int i = 1; i &lt;= 10; i++) {
        thisSum += arr[i];
        if (thisSum &gt; maxSum) {
            maxSum = thisSum;
        }
        else if (thisSum &lt; maxSum) {
            thisSum = 0;
        }
    }

    cout &lt;&lt; maxSum;

    return 0;
}</code></pre><ul>
<li>linear time의 장점</li>
</ul>
<ol>
<li>메모리 효율<ul>
<li>스트리밍처럼 한 번 쭉 읽으면서 처리가 가능하기 때문에</li>
<li>배열 전체를 메모리에 다 올릴 필요가 없다.</li>
<li>데이터를 앞에서부터 한 번씩만 읽으면서 처리 가능하다.</li>
</ul>
</li>
<li>실시간 처리 (Online)<ul>
<li>데이터를 끝까지 다 보지 않아도, 지금까지 읽은 부분에 대해서는 항상 정답을 알고 있다.</li>
<li>즉, 중간 과정에서도 항상 지금까지의 정답이 유지되고 있다.</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Methods for solving Recurrences]]></title>
            <link>https://velog.io/@dev_noob8933/Recurrences</link>
            <guid>https://velog.io/@dev_noob8933/Recurrences</guid>
            <pubDate>Tue, 14 Apr 2026 10:05:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>왜 갑자기 재귀?</p>
</blockquote>
<ul>
<li>Divide and Conquer 알고리즘의 수행 시간을 분석하는 데 가장 자연스러운 접근법.</li>
</ul>
<h1 id="methods-for-solving-recurrences">Methods for solving Recurrences</h1>
<ol>
<li>Brute-force method</li>
<li>Substitution method</li>
<li>Recursion tree method</li>
<li>Master method</li>
</ol>
<blockquote>
</blockquote>
<h2 id="inequality-recurrences">Inequality Recurrences</h2>
<p>만약 T(n) = 2 * T(n/2) + Theta(n) 이런 식으로 주어졌다면 Theta notation 사용이 가능하지만, 부등식일 경우 불가능하다.</p>
<ul>
<li>T(n) &lt;= 2 * T(n/2) + Theta(n)</li>
<li><blockquote>
<p>상한이 주어졌으므로 Big-O notation 사용.</p>
</blockquote>
</li>
<li>T(n) &gt;= 2 * T(n/2) + theta(n)</li>
<li><blockquote>
<p>하한이 주어졌으므로 Big-Omega notation 사용.</p>
</blockquote>
</li>
</ul>
<h2 id="brute-force-method">Brute-force Method</h2>
<pre><code>[ex 1]
T(n) = 2 * T(n/2) + n
     = 2 * (2 * T(n/4) + n/2) + n = 2^2 * T(n/2^2) + 2n
     = 2 * (2 * (2 * T(n/8) + n/4) + n/2) + n = 2^3 * T(n/2^3) + 3n
     ...
     = 2^k * T(n/2^k) + kn = n*T(1) + log(n) * n
     = O(nlogn)

[ex 2]
T(n) = 2 * T(n/2) + n
T(n)/n = T(n/2)/(n/2) + 1
T(n/2)/(n/2) = T(n/4)/(n/4) + 1
...
T(2)/2 = T(1)/1 + 1

T(n)/n = T(1)/1 + 1 + 1 + 1 + ... + 1 + 1 = n + logn
T(n) = nlogn</code></pre><h2 id="substitution-method">Substitution Method</h2>
<ol>
<li>Guess the form of the solution.</li>
<li>Use mathematical induction to find the costants and show that the solution works.</li>
</ol>
<ul>
<li>만약 어떤 상수나 낮은 차수에 의해 기존의 증명하려던 식이 성립하지 않을 경우, 해당 차수를 관리할 수 있는 임의의 조작을 해주어야 한다.</li>
</ul>
<blockquote>
</blockquote>
<h3 id="avoiding-pitfalls">Avoiding Pitfalls</h3>
<p>귀납 증명은 &quot;정확히 같은 형태&quot;로 돌아와야 한다.</p>
<h3 id="changing-variables">Changing Variables</h3>
<p>점화식이 이상하면 변수 치환으로 바꿔라.</p>
<hr>
<h2 id="recursion-tree-method">Recursion-tree Method</h2>
<p>재귀 호출 구조를 트리 형태로 표현한 뒤, 각 레벨에서 발생하는 총 비용을 계산하고 이를 모두 더하여 전체 수행 시간을 구하는 방법이다.</p>
<hr>
<h2 id="master-method">Master Method</h2>
<blockquote>
</blockquote>
<p>T(n) = a*T(n/b) + f(n)에서 <code>a &gt;= 1</code>, <code>b &gt; 1</code>인 상수이며 f(n)이 점근적으로 양수 함수일 때 사용하는 방법.</p>
<blockquote>
</blockquote>
<p>a가 1보다 크거나 같아야 하고, b가 1보다 커야 하는 이유가 뭘까.
결국 b가 1보다 커야 문제가 기존보다 작은 부분문제로 감소하기 때문이며, a역시 1보다 크거나 같아야 하는 이유는 주어진 문제를 적어도 1회 이상은 온전히 푸는 구조여야 Divide and Conquer의 구조를 유지하기 때문인가?</p>
<blockquote>
</blockquote>
<p>b는 몇 개로 쪼개질 것인가, a는 하위 문제의 개수가 몇 개인가를 담당해야 하기에 저런 조건을 세운 듯 하다.</p>
<blockquote>
<p>n^log_b(a) 이 식은 결국 Divide and Conquer를 하는 과정에서 생기는 총 작업량을 의미한다. 또한, f(n)은 각 레벨에서의 노드 처리 작업량을 의미한다. </p>
</blockquote>
<p>따라서, 두 식을 비교하여 더 큰 값이 있다면 해당 값이 다른 값을 무시할 수 있기에 해당 값이 답인 것이고, 두 값이 같다면 곱해주어야 한다. </p>
<blockquote>
</blockquote>
<p>Master method를 보면 Divide and Conquer 알고리즘의 핵심 골격을 알 수 있는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Growth of Functions]]></title>
            <link>https://velog.io/@dev_noob8933/Growth-of-Functions</link>
            <guid>https://velog.io/@dev_noob8933/Growth-of-Functions</guid>
            <pubDate>Tue, 14 Apr 2026 07:27:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>We can predict how an algorithm will perform for large input sets, based on its performance for moderate input sets.</p>
</blockquote>
<h1 id="big-θ">Big-Θ</h1>
<blockquote>
</blockquote>
<p>Θ(g(n)) = { f(n): there exist <strong>positive</strong> c1, c2, and n0 such that 0 &lt;= c1*g(n) &lt;= f(n) &lt;= c2*g(n) for all n &gt;= n0 }.</p>
<ul>
<li>집합이므로 f(n)이 Θ(g(n))에 포함된다고 표기할 수 있다. 다만 표기상 f(n) = Θ(g(n))이라고 한다.</li>
<li>Θ(g(n))에 속하는 모든 함수 f(n)은
충분히 큰 n에 대해 0 이상이어야 한다. <ul>
<li>왜냐, 어차피 성장률 비교이기 때문에.</li>
<li>만약 음수가 된다면, Θ(g(n))은 공집합이 된다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="big-o">Big-O</h1>
<blockquote>
</blockquote>
<p>Θ에서 하한이 없어진 버전.
O(g(n)) = {f(n) : there exist positive constants c, n0 such that 0 &lt;= f(n) &lt;= c*g(n) for all n &gt;= n0}.</p>
<hr>
<h1 id="big-ω">Big-Ω</h1>
<blockquote>
</blockquote>
<p>Θ에서 상한이 없어진 버전.
Ω(g(n)) = {f(n) : there exist positive constants c, n0 such that c*g(n) &lt;= f(n) for all n &gt;= n0}.</p>
<h3 id="properties">Properties</h3>
<ul>
<li>if T1(n) = O(f(n)) &amp;&amp; T2(n) = O(g(N)),<ul>
<li>T1(n) + T2(n) = max(O(f(n)), O(g(n))</li>
</ul>
</li>
<li>T1(n) * T2(n) = O(f(n) * g(n))</li>
<li>O(c * f(n)) = O(f(n))<h3 id="names">Names</h3>
</li>
<li>O(n^3) : Cubic</li>
<li>O(n^2) : Quadratic</li>
<li>O(nlogn)</li>
<li>O(n) : Linear</li>
</ul>
<hr>
<h2 id="theorem-31">Theorem 3.1</h2>
<blockquote>
</blockquote>
<p>For any two functions f(n) and g(n), we have f(n) = Θ(g(n)) iff f(n) = O(g(n)) and f(n) = Ω(g(n)).
-&gt; 즉, 하한과 상한의 함수가 같아야 세타가 존재한다.</p>
<hr>
<h2 id="growth-compare">Growth compare</h2>
<blockquote>
<p>log(n) &lt; n &lt; nlog(n) &lt; n^2 &lt; 2^n</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Correctness proof by using Loop Invariants 2]]></title>
            <link>https://velog.io/@dev_noob8933/Correctness-proof-by-using-Loop-invariant-2</link>
            <guid>https://velog.io/@dev_noob8933/Correctness-proof-by-using-Loop-invariant-2</guid>
            <pubDate>Mon, 13 Apr 2026 09:48:49 GMT</pubDate>
            <description><![CDATA[<h2 id="merge-sort">Merge Sort</h2>
<h3 id="divide-and-conquer">Divide and Conquer</h3>
<blockquote>
<p>핵심 성질</p>
</blockquote>
<ul>
<li>Divide : 더 작은 동일 문제로 나눈다.</li>
<li>Conquer : 재귀로 해결한다.</li>
<li>Combine : 부분해를 combine하여 전체해로 만든다.</li>
</ul>
<p>merge sort에서의 Divide and conquer 접목</p>
<p><strong>Divide</strong>
-&gt; 정렬할 n개의 원소로 이루어진 수열을 각각 n/2개의 원소를 가지는 2개의 부분수열로 나눈다.</p>
<p><strong>Conquer</strong>
-&gt; 두 개의 부분수열을 merge sort을 통해 재귀적으로 정렬한다.</p>
<p><strong>Combine</strong>
-&gt; 두 개의 정렬된 부분수열을 merge하여 전체가 정렬된 정답을 만든다.</p>
<hr>
<h3 id="merge-procedure">Merge Procedure</h3>
<blockquote>
<ul>
<li>It merges them to form a single sorted subarray that replaces the current subarray A[p...r].</li>
</ul>
</blockquote>
<ul>
<li>we assume that the subarray A[p...q] and A[q+1...r] are in sorted order.</li>
<li>MERGE(A, p, q, r) 보조 함수로 해당 기능을 수행한다.</li>
</ul>
<pre><code>MERGE(A, p, q, r)    A : p ~ r
n1 = q - p + 1
n2 = r - q

Let L = [1 ... n1 + 1] and R = [1 ... n2 + 1]
for i = 1 to n1
    L[i] = A[p + i - 1]
for j = 1 to n2
    R[j] = A[q + j]

L[n1 + 1] = Inf
R[n2 + 1] = Inf
i = 1
j = 1
for k = p to r
    if L[i] &lt;= R[j]
        A[k] = L[i]
        i++
    else
        A[k] = R[j]
        j++</code></pre><hr>
<h3 id="correctness-of-merge-procedure">Correctness of Merge Procedure</h3>
<blockquote>
<ul>
<li>Loop Invariant(L.I)</li>
<li><blockquote>
<p>At the start of each iteration for the merging loop, the subarray A[p...k-1] contains the (k - p) smallest elements of L[1...n1+1] and R[1...n2+1], in ascending order.</p>
</blockquote>
</li>
<li><blockquote>
<p>Moreover, L[i] and R[j] is the smallest element of their arrays that have not been copied back into A.</p>
</blockquote>
</li>
</ul>
</blockquote>
<h4 id="initialization">Initialization</h4>
<blockquote>
</blockquote>
<ul>
<li>첫 루프 진입 전 k는 p이고, i와 j는 모두 1이다. 이를 Loop invariant에 넣어보자.<ul>
<li>At the start of each iteration for the merging loop, the subarray A[p...p-1] contains the (p - p = 0) smallest elements of L[1...n1+1] and R[1...n2+1], in ascending order.</li>
<li><blockquote>
<p>A[p...p-1]은 공집합이다.</p>
</blockquote>
</li>
<li><blockquote>
<p>이 집합은 0개의 가장 작은 원소들(in L, R)을 갖고 있다.</p>
</blockquote>
</li>
<li>Moreover, L[1] and R[1] is the smallest element of their arrays that have not been copied back into A.</li>
<li><blockquote>
<p>A는 공집합이므로 아무것도 복사되지 않았으며, 처음에 merge procedure에서 &quot;we assume that the subarray A[p...q] and A[q+1...r] are in sorted order.&quot;를 언급했으므로 L[1]과 R[1]은 각 배열에서 복사되지 않은 가장 작은 수임을 보장한다.</p>
</blockquote>
</li>
</ul>
</li>
<li><em>따라서 Initialization은 L.I를 만족한다.*</em></li>
</ul>
<h4 id="maintenance">Maintenance</h4>
<p>복습 : &quot;If it is true before an iteration of the loop, it remains true before the next iteration.</p>
<blockquote>
</blockquote>
<ul>
<li>Loop 내에 두 가지 경우가 존재하므로 각각에 대해 증명해야 한다.</li>
</ul>
<ol>
<li>When L[i] &lt;= R[j]<ul>
<li>루프 시작 전에 L.I가 만족한다고 하자.<ul>
<li>At the start of each iteration for the merging loop, the subarray A[p...k-1] contains the (k-p) smallest elements of L[1...n1+1] and R[1...n2+1], in ascending order.</li>
<li>Moreover, L[i] and R[j] is the smallest element of their arrays that have not been copied back into A.</li>
<li><blockquote>
<p>L[i]는 A에 복사되지 않은 가장 작은 원소이다.</p>
</blockquote>
</li>
<li><blockquote>
<p>A[p...k-1]이 k-p개의 가장 작은 원소들을 갖고 있기 때문에, L[i]를 A[k]에 넣게 되면 A[p..k]는 k-p+1개의 가장 작은 원소들을 갖고 있게 된다.</p>
</blockquote>
</li>
<li><blockquote>
<p>이제 다음 iteration 시작 시, k와 i가 증가하면 L.I가 다시 만족한다.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
</li>
<li>When L[i] &gt; R[j]<ul>
<li>위의 경우와 같다.<blockquote>
</blockquote>
</li>
</ul>
</li>
</ol>
<p><strong>따라서 Maintenance는 L.I를 만족한다.</strong></p>
<h4 id="termination">Termination</h4>
<blockquote>
</blockquote>
<ul>
<li>루프 탈출 후 k = r + 1이다.</li>
<li>이를 L.I에 대입해보자.<ul>
<li>the array A[p...r] contains the (r-p+1) smallest elements of L[1...n1+1] and R[1...n2+1], in sorted order.</li>
<li><blockquote>
<p>L과 R의 총 사이즈는 <code>n1 + n2 + 2 = r - p + 3</code> 이다.</p>
</blockquote>
</li>
<li><blockquote>
<p>아직 복사되지 않은 두 개의 원소들은 각 배열의 가장 큰 원소이자 sentinel인 원소들이다.</p>
</blockquote>
</li>
<li>Moreover, L[n1+1] and L[n2+1] is the smallest element of their arrays that have not been copied back into A.</li>
<li><blockquote>
<p>L[n1+1]과 R[n2+1]은 각각 Inf이며, 복사되지 않은 유일한 원소이다.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="merge-sort-time-complexity">Merge Sort Time Complexity</h3>
<ul>
<li>Divide : 항상 가운데 위치만 계산하므로 Theta(1)</li>
<li>Conquer : 사이즈가 n/2인 부분문제 2개를 해결하므로 2 * T(n/2)</li>
<li>Combine : MERGE procedure에서 본 바와 같이, n개의 element에 대해 Theta(n)이 걸린다.</li>
</ul>
<p>-&gt; T(1) = 1, T(n) = 2 * T(n/2) + n
-&gt; ... T(n) = nlog(n) + n</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Correctness proof by using Loop Invariants 1]]></title>
            <link>https://velog.io/@dev_noob8933/Correctness-proof-by-using-Loop-Invariants-1</link>
            <guid>https://velog.io/@dev_noob8933/Correctness-proof-by-using-Loop-Invariants-1</guid>
            <pubDate>Mon, 13 Apr 2026 06:54:19 GMT</pubDate>
            <description><![CDATA[<h1 id="sortings">Sortings</h1>
<ul>
<li>Insertion sort, merge sort, quick sort 등의 다양한 정렬 알고리즘들이 있다.</li>
</ul>
<h2 id="insertion-sort">Insertion Sort</h2>
<blockquote>
<ul>
<li>Incremental algorithm이다.</li>
<li>앞의 원소부터 차례대로 증가하며 체크한다.</li>
</ul>
</blockquote>
<p>그렇다면, 이 알고리즘 방식이 정말로 내가 원하는대로 정렬해주는가?</p>
<hr>
<h3 id="loop-invariants">Loop Invariants</h3>
<blockquote>
<ul>
<li>어떤 알고리즘이 주어졌을 때, 왜 이 알고리즘의 correctess를 보이기 위해 사용하는 방식</li>
<li>루프가 반복되는 동안 항상 유지되는 성질 </li>
<li><em>Initialization*</em><ul>
<li>It is true prior to the first iteration of the loop.</li>
</ul>
</li>
<li><em>Maintenance*</em><ul>
<li>If it is true before an iteration of the loop, it remains true before the next iteration.
<strong>Termination</strong><ul>
<li>When the loop terminates, the invariant gives us a useful property that helps show that the algorithm is correct.</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
<p>Insertion sort를 통해 Loop Invariant를 활용한 correctness 증명을 적용해보자.</p>
<hr>
<h3 id="insertion-sort에서의-loop-invariant">Insertion Sort에서의 Loop invariant</h3>
<blockquote>
<p>At the start of the loop, the subarray A[1...j-1] consists of the elements originally in A[1...j-1] but in ascending order.</p>
</blockquote>
<p><strong>Initialization</strong></p>
<ul>
<li>첫 반복 전에 j는 2이다. 이를 loop invariant에 대입해보자.<ul>
<li>At the start of the loop, A[1...1] consists of the elements originally in A[1..1] but in ascending order.</li>
<li><blockquote>
<p>참</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<p><strong>Maintenance</strong></p>
<ul>
<li>Maintenance에서 보여야 하는 성질의 핵심은 아래와 같다.<ul>
<li>루프 실행 전에도 참이라면, 다음 루프 실행 전에도 참이어야 한다.</li>
<li><blockquote>
<p>루프 실행 전에도 Loop invariant를 만족한다면, 다음 루프 실행 전에도 이 Loop invariant를 만족해야 한다.</p>
</blockquote>
</li>
</ul>
</li>
<li>그러므로, 루프 실행 전에 Loop invariant를 만족한다고 하자.<ul>
<li>At the start of the j-th iteration of the loop, A[1...j-1] consists of the elements originally in A[1...j-1] but in ascending order.</li>
</ul>
</li>
<li>A[1...j-1]를 j까지로 확장하는 것이다.<ul>
<li>변수 i를 통해 A[j-1]부터 A[1]까지 순회하며 A[j]보다 큰 원소들을 오른쪽으로 옮긴다. 해당 과정이 멈춘 경우, A[j]는 멈춘 위치 +1인 곳(i+1)에 삽입된다. 따라서 정렬된 A[1...j-1]의 배열에 key를 올바른 위치에 삽입한다. 즉, 삽입된 위치에는 A[j]가 있고, A[j] 기준으로 왼쪽은 A[j]보다 작거나 같은 값들, 오른쪽은 A[j]보다 큰 값들이 존재하므로 기존 배열의 정렬성을 해치지 않고 올바르게 삽입된다.</li>
</ul>
</li>
<li>이제, j를 증가시켜서 A[1...j]로 만들면, 이 배열은 다음 iteration 시작 시에도 정렬된 상태이므로 Loop Invariant가 유지된다.</li>
<li><blockquote>
<p>따라서 Maintenance 역시 참이다.</p>
</blockquote>
</li>
</ul>
<p><strong>Termination</strong></p>
<ul>
<li>for loop이 종료되었을 때 j = n + 1이다.</li>
<li>이 j 값을 loop invariant에 넣어보자.<ul>
<li>A[1...n] consists of the elements originally in A[1...j] but in ascending order.</li>
<li><blockquote>
<p>correctness 증명 완료.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 19236번 : 청소년 상어 복습]]></title>
            <link>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-19236%EB%B2%88-%EC%B2%AD%EC%86%8C%EB%85%84-%EC%83%81%EC%96%B4-%EB%B3%B5%EC%8A%B5-urj4go8w</link>
            <guid>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-19236%EB%B2%88-%EC%B2%AD%EC%86%8C%EB%85%84-%EC%83%81%EC%96%B4-%EB%B3%B5%EC%8A%B5-urj4go8w</guid>
            <pubDate>Sat, 11 Apr 2026 11:40:27 GMT</pubDate>
            <description><![CDATA[<p>sol : 67&#39; 43&#39;&#39;</p>
<ul>
<li>수행 시간 : 0ms</li>
<li>메모리 : 2024KB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>DFS시 매번 독립된 분기 데이터들을 직접 다뤄야 하므로 <code>&amp;</code> 잊지말자.</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

#define SHARK -1
#define EMPTY 0

const int ds[9][2] = {
    {},
    {-1, 0},
    {-1, -1},
    {0, -1},
    {1, -1},
    {1, 0},
    {1, 1},
    {0, 1},
    {-1, 1}
};

struct Fish {
    int i;
    int j;
    int id;
    int dir;
    bool onGrid;

    Fish() {}
    Fish(int _i, int _j, int _id, int _dir) :
        i(_i), j(_j), id(_id), dir(_dir), onGrid(true) { }
};

struct Shark {
    int i;
    int j;
    int dir;
    int score;

    Shark() {}
    Shark(int _i, int _j, int _dir, int _score) :
        i(_i), j(_j), dir(_dir), score(_score) { }
};

int grid[5][5];
vector&lt;Fish&gt; fishes;
Shark shark;
int maxScore;

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= 4 &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= 4;
}

void DupGrid(int from[5][5], int to[5][5]) {
    for (int i = 1; i &lt;= 4; i++) {
        for (int j = 1; j &lt;= 4; j++) {
            to[i][j] = from[i][j];
        }
    }
}

void FishMove(int g[5][5], vector&lt;Fish&gt;&amp; fs, Shark&amp; s) {
    for (int f = 1; f &lt;= 16; f++) {
        if (!fs[f].onGrid) continue;

        int ci = fs[f].i, cj = fs[f].j, cd = fs[f].dir;
        int ni = ci + ds[cd][0], nj = cj + ds[cd][1];
        int cnt = 0;
        while (!InGrid(ni, nj) || g[ni][nj] == SHARK) {
            cd = (cd + 1 &gt; 8 ? 1 : cd + 1);
            ni = ci + ds[cd][0], nj = cj + ds[cd][1];
            cnt++;
            if (cnt &gt;= 8) break;
        }
        if (cnt &gt;= 8) continue;

        if (g[ni][nj] != EMPTY) {
            int temp = g[ni][nj];
            g[ni][nj] = f;
            fs[f].i = ni, fs[f].j = nj, fs[f].dir = cd;

            g[ci][cj] = temp;
            fs[temp].i = ci, fs[temp].j = cj;
        }
        else {
            g[ci][cj] = EMPTY;
            g[ni][nj] = f;
            fs[f].i = ni, fs[f].j = nj, fs[f].dir = cd;
        }
    }
}

void Dfs(int g[5][5], vector&lt;Fish&gt;&amp; fs, Shark&amp; s) {
    maxScore = max(maxScore, s.score);

    FishMove(g, fs, s);

    int ci = s.i, cj = s.j, cd = s.dir;

    for (int d = 1; d &lt;= 3; d++) {
        int ni = ci + ds[cd][0] * d, nj = cj + ds[cd][1] * d;
        if (!InGrid(ni, nj)) continue;
        if (g[ni][nj] == EMPTY) continue;

        int tempGrid[5][5];
        DupGrid(g, tempGrid);
        vector&lt;Fish&gt; tempFishes = fs;
        Shark tempShark = s;

        tempGrid[tempShark.i][tempShark.j] = EMPTY;
        tempShark.i = ni, tempShark.j = nj, tempShark.dir = tempFishes[tempGrid[ni][nj]].dir;
        tempShark.score += tempGrid[ni][nj];
        tempFishes[tempGrid[ni][nj]].onGrid = false;
        tempGrid[ni][nj] = SHARK;

        Dfs(tempGrid, tempFishes, tempShark);
    }
}

int main() {
    fishes.resize(17);
    for (int i = 1; i &lt;= 4; i++) {
        for (int j = 1; j &lt;= 4; j++) {
            int id, dir;
            cin &gt;&gt; id &gt;&gt; dir;
            grid[i][j] = id;
            fishes[id] = Fish(i, j, id, dir);
        }
    }

    shark = Shark(1, 1, fishes[grid[1][1]].dir, grid[1][1]);
    fishes[grid[1][1]].onGrid = false;
    grid[1][1] = SHARK;

    int tempGrid[5][5];
    DupGrid(grid, tempGrid);
    vector&lt;Fish&gt; tempFishes = fishes;
    Shark tempShark = shark;

    Dfs(tempGrid, tempFishes, tempShark);

    cout &lt;&lt; maxScore;

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2024_하_P_1_L15 복습]]></title>
            <link>https://velog.io/@dev_noob8933/2024%ED%95%98P1L15-%EB%B3%B5%EC%8A%B5</link>
            <guid>https://velog.io/@dev_noob8933/2024%ED%95%98P1L15-%EB%B3%B5%EC%8A%B5</guid>
            <pubDate>Sat, 11 Apr 2026 06:44:42 GMT</pubDate>
            <description><![CDATA[<h1 id="메두사와-전사들">메두사와 전사들</h1>
<h2 id="bfs-simulation">BFS, Simulation</h2>
<p>평균 : 180&#39;</p>
<hr>
<p>sol : 235&#39; 27&#39;&#39;</p>
<ul>
<li>수행 시간 : 13ms</li>
<li>메모리 : 0MB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>두 번째 푸는데도 4시간 걸리는 미친 문제</li>
<li>주 병목은 부채꼴 진행</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;utility&gt;
#include &lt;queue&gt;
#include &lt;tuple&gt;
#include &lt;cmath&gt;
using namespace std;

// 0-based
#define EMPTY 0
#define MAX_N 60
#define ROAD 0
#define NOT_ROAD 1

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

int roadGrid[MAX_N][MAX_N];
int solderNumGrid[MAX_N][MAX_N];
pair&lt;int, int&gt; fromWhere[MAX_N][MAX_N];
bool bestVision[MAX_N][MAX_N];
vector&lt;pair&lt;int, int&gt;&gt; bestStunned;

int movedDist, stoneCnt, attackCnt;

// 상, 하, 좌, 우
const int ds[4][2] = {
    {-1, 0},
    {1, 0},
    {0, -1},
    {0, 1}
};

int n, m;
struct Medusa {
    int si;
    int sj;
    int ei;
    int ej;
    pair&lt;int, int&gt; curPos;
    vector&lt;pair&lt;int, int&gt;&gt; path;

    Medusa() :
        si(-1), sj(-1), ei(-1), ej(-1), curPos({ -1, -1 }) {
    }
};

struct Solder {
    int i;
    int j;
    bool stone;
    bool onGrid;

    Solder() {};
};

Medusa medusa;
vector&lt;Solder&gt; solders;

void DebugVision(bool v[MAX_N][MAX_N]) {
    cout &lt;&lt; endl &lt;&lt; &quot;VISION&quot; &lt;&lt; endl;
    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            cout &lt;&lt; v[i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; endl;
}

void Debug() {
    cout &lt;&lt; endl;
    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            if (solderNumGrid[i][j] &gt; 0) cout &lt;&lt; &quot;1 &quot;;
            else if (i == medusa.curPos.first &amp;&amp; j == medusa.curPos.second) cout &lt;&lt; &quot;M &quot;;
            else cout &lt;&lt; &quot;0 &quot;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; endl;
}

void Init() {
    cin &gt;&gt; n &gt;&gt; m;

    cin &gt;&gt; medusa.si &gt;&gt; medusa.sj &gt;&gt; medusa.ei &gt;&gt; medusa.ej;

    solders.resize(m + 1);
    for (int s = 1; s &lt;= m; s++) {
        int ai, aj;
        cin &gt;&gt; ai &gt;&gt; aj;
        solders[s].i = ai;
        solders[s].j = aj;
        solders[s].stone = false;
        solders[s].onGrid = true;
        solderNumGrid[ai][aj]++;
    }

    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            cin &gt;&gt; roadGrid[i][j];
        }
    }
}

bool InGrid(int i, int j) {
    return 0 &lt;= i &amp;&amp; i &lt; n &amp;&amp; 0 &lt;= j &amp;&amp; j &lt; n;
}

void InitFromWhere() {
    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            fromWhere[i][j] = { -1, -1 };
        }
    }
}

void InitBestVision() {
    for (int i = 0; i &lt; n; i++) {
        for (int j = 0; j &lt; n; j++) {
            bestVision[i][j] = false;
        }
    }
}

bool GetMedusaPath() {
    bool possible = false;
    InitFromWhere();

    queue&lt;pair&lt;int, int&gt;&gt; q;
    q.push({ medusa.si, medusa.sj });
    fromWhere[medusa.si][medusa.sj] = { medusa.si, medusa.sj };

    while (!q.empty()) {
        int ci, cj;
        tie(ci, cj) = q.front();
        q.pop();

        if (ci == medusa.ei &amp;&amp; cj == medusa.ej) {
            possible = true;
            break;
        }

        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + ds[d][0], nj = cj + ds[d][1];

            if (!InGrid(ni, nj)) continue;
            if (fromWhere[ni][nj] != make_pair(-1, -1)) continue;
            if (roadGrid[ni][nj] == NOT_ROAD) continue;

            fromWhere[ni][nj] = { ci, cj };
            q.push({ ni, nj });
        }
    }

    if (!possible) return false;

    vector&lt;pair&lt;int, int&gt;&gt; reversePath;
    pair&lt;int, int &gt; curPos = { medusa.ei, medusa.ej };

    while (curPos != make_pair(medusa.si, medusa.sj)) {
        reversePath.push_back(curPos);
        curPos = fromWhere[curPos.first][curPos.second];
    }

    medusa.path.push_back({ medusa.si, medusa.sj });
    for (int i = reversePath.size() - 1; i &gt;= 0; i--) {
        medusa.path.push_back(reversePath[i]);
    }

    return true;
}

int Oppos(int dir) {
    if (dir == UP) return DOWN;
    if (dir == DOWN) return UP;
    if (dir == LEFT) return RIGHT;
    if (dir == RIGHT) return LEFT;
}

void GetBestDir() {
    int bestCnt = 0;
    InitBestVision();

    // 상,하,좌,우 순으로 바라볼 때
    for (int curD = 0; curD &lt; 4; curD++) {
        // 위치, 방향
        queue&lt;tuple&lt;int, int, int, int&gt;&gt; caught;

        bool visited[MAX_N][MAX_N];
        bool vision[MAX_N][MAX_N];
        for (int i = 0; i &lt; n; i++) {
            for (int j = 0; j &lt; n; j++) {
                visited[i][j] = false;
                vision[i][j] = false;
            }
        }

        // Phase 1. 전사 탐색
        queue&lt;tuple&lt;int, int, int, int&gt;&gt; q;
        int ci, cj;
        tie(ci, cj) = medusa.curPos;
        for (int sightD = 0; sightD &lt; 4; sightD++) {
            if (sightD == Oppos(curD)) continue;
            int ni, nj;
            if (sightD == curD) {
                ni = ci + ds[sightD][0], nj = cj + ds[sightD][1];
            }
            else {
                ni = ci + ds[sightD][0] + ds[curD][0], nj = cj + ds[sightD][1] + ds[curD][1];
            }

            if (!InGrid(ni, nj)) continue;
            if (visited[ni][nj]) continue;
            visited[ni][nj] = true,    vision[ni][nj] = true;
            q.push(make_tuple(ni, nj, sightD, curD));
            if (solderNumGrid[ni][nj] &gt; 0) caught.push(make_tuple(ni, nj, sightD, curD));
        }



        while (!q.empty()) {
            int ci, cj, sightD, curD;
            tie(ci, cj, sightD, curD) = q.front();
            q.pop();

            // 직선인 경우
            if (sightD == curD) {
                int ni = ci + ds[sightD][0], nj = cj + ds[sightD][1];
                if (!InGrid(ni, nj)) continue;
                if (visited[ni][nj]) continue;
                visited[ni][nj] = true, vision[ni][nj] = true;

                q.push({ ni, nj, sightD, curD });
                if (solderNumGrid[ni][nj] &gt; 0) caught.push(make_tuple(ni, nj, sightD, curD));
            }
            // 대각인 경우
            else {
                int ni1 = ci + ds[curD][0], nj1 = cj + ds[curD][1];
                int ni2 = ni1 + ds[sightD][0], nj2 = nj1 + ds[sightD][1];

                if (InGrid(ni1, nj1) &amp;&amp; !visited[ni1][nj1]) {
                    q.push({ ni1, nj1, sightD, curD });
                    visited[ni1][nj1] = true;
                    vision[ni1][nj1] = true;
                    if (solderNumGrid[ni1][nj1] &gt; 0)    caught.push({ ni1, nj1, sightD, curD });
                }
                if (InGrid(ni2, nj2) &amp;&amp; !visited[ni2][nj2]) {
                    q.push({ ni2, nj2, sightD, curD });
                    visited[ni2][nj2] = true;
                    vision[ni2][nj2] = true;
                    if (solderNumGrid[ni2][nj2] &gt; 0)    caught.push({ ni2, nj2, sightD, curD });
                }
            }
        }

        // Phase 2. 전사 위치에서 다시 끄기
        vector&lt;pair&lt;int, int&gt;&gt; stunned;
        while (!caught.empty()) {
            int i, j, sightD, curD;
            tie(i, j, sightD, curD) = caught.front();

            caught.pop();
            for (int i = 0; i &lt; n; i++) {
                for (int j = 0; j &lt; n; j++) {
                    visited[i][j] = false;
                }
            }

            // 뒤에 가려진 전사
            if (!vision[i][j]) continue;

            // 실제로 굳은 전사들의 위치
            stunned.push_back({ i, j });

            // 직선 방향인 경우
            if (sightD == curD) {
                int ci = i, cj = j;
                while (InGrid(ci, cj)) {
                    ci += ds[sightD][0], cj += ds[sightD][1];
                    vision[ci][cj] = false;
                }
                continue;
            }

            // 대각 방향인 경우
            queue&lt;pair&lt;int, int&gt;&gt; sq;
            sq.push({ i, j });
            visited[i][j] = true;

            while (!sq.empty()) {
                int ci, cj;
                tie(ci, cj) = sq.front();
                sq.pop();

                int    ni1 = ci + ds[curD][0], nj1 = cj + ds[curD][1];
                int ni2 = ni1 + ds[sightD][0], nj2 = nj1 + ds[sightD][1];

                if (InGrid(ni1, nj1) &amp;&amp; !visited[ni1][nj1]) {
                    sq.push({ ni1, nj1 });
                    visited[ni1][nj1] = true;
                    vision[ni1][nj1] = false;
                }
                if (InGrid(ni2, nj2) &amp;&amp; !visited[ni2][nj2]) {
                    sq.push({ ni2, nj2 });
                    visited[ni2][nj2] = true;
                    vision[ni2][nj2] = false;
                }
            }
        }

        int curCnt = 0;
        for (int i = 0; i &lt; n; i++) {
            for (int j = 0; j &lt; n; j++) {
                if (vision[i][j]) {
                    curCnt += solderNumGrid[i][j];
                }
            }
        }

        if (curCnt &gt; bestCnt) {
            bestCnt = curCnt;
            for (int i = 0; i &lt; n; i++) {
                for (int j = 0; j &lt; n; j++) {
                    bestVision[i][j] = vision[i][j];
                }
            }
            bestStunned = stunned;
        }
    }
}

void Stun() {
    for (int a = 0; a &lt; bestStunned.size(); a++) {
        for (int s = 1; s &lt;= m; s++) {
            if (!solders[s].onGrid) continue;
            if (solders[s].i == bestStunned[a].first &amp;&amp; solders[s].j == bestStunned[a].second) {
                solders[s].stone = true;
                stoneCnt++;
            }
        }
    }
}

int Dist(int si, int sj) {
    int mi, mj;
    tie(mi, mj) = medusa.curPos;

    return abs(si - mi) + abs(sj - mj);
}

void SoldersMove() {
    for (int s = 1; s &lt;= m; s++) {
        if (!solders[s].onGrid) continue;
        if (solders[s].stone) {
            solders[s].stone = false;
            continue;
        }

        int ci = solders[s].i, cj = solders[s].j;
        int minDist = Dist(ci, cj);
        int best_i = -1, best_j = -1;
        // 1st move
        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + ds[d][0], nj = cj + ds[d][1];
            if (!InGrid(ni, nj)) continue;
            if (bestVision[ni][nj]) continue;
            if (Dist(ni, nj) &gt;= minDist) continue;

            minDist = Dist(ni, nj);
            best_i = ni, best_j = nj;
        }

        if (best_i == -1) continue;
        solderNumGrid[solders[s].i][solders[s].j]--;
        solders[s].i = best_i, solders[s].j = best_j;
        solderNumGrid[solders[s].i][solders[s].j]++;
        movedDist++;

        // check
        if (solders[s].i == medusa.curPos.first &amp;&amp; solders[s].j == medusa.curPos.second) {
            attackCnt++;
            solders[s].onGrid = false;
            solderNumGrid[solders[s].i][solders[s].j]--;
            continue;
        }

        // 2nd move
        int tempDs[4][2] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };
        ci = solders[s].i, cj = solders[s].j;
        minDist = Dist(ci, cj);
        best_i = -1, best_j = -1;
        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + tempDs[d][0], nj = cj + tempDs[d][1];
            if (!InGrid(ni, nj)) continue;
            if (bestVision[ni][nj]) continue;
            if (Dist(ni, nj) &gt;= minDist) continue;

            minDist = Dist(ni, nj);
            best_i = ni, best_j = nj;
        }

        if (best_i == -1) continue;
        solderNumGrid[solders[s].i][solders[s].j]--;
        solders[s].i = best_i, solders[s].j = best_j;
        solderNumGrid[solders[s].i][solders[s].j]++;
        movedDist++;

        // check
        if (solders[s].i == medusa.curPos.first &amp;&amp; solders[s].j == medusa.curPos.second) {
            attackCnt++;
            solders[s].onGrid = false;
            solderNumGrid[solders[s].i][solders[s].j]--;
        }
    }
}

void MedusaMove(int t) {
    medusa.curPos = medusa.path[t];
    int ci, cj;
    tie(ci, cj) = medusa.curPos;
    if (solderNumGrid[ci][cj] &gt; 0) {
        solderNumGrid[ci][cj] = 0;
        for (int s = 1; s &lt;= m; s++) {
            if (!solders[s].onGrid) continue;
            if (solders[s].i == ci &amp;&amp; solders[s].j == cj) {
                solders[s].onGrid = false;
            }
        }
    }
}

int main() {
    Init();

    if (!GetMedusaPath()) {
        cout &lt;&lt; -1;
        return 0;
    }

    medusa.curPos = medusa.path[0];
    for (int t = 1; t &lt; medusa.path.size() - 1; t++) {
        movedDist = 0, attackCnt = 0, stoneCnt = 0;

        MedusaMove(t);

        GetBestDir();

        Stun();

        SoldersMove();

        cout &lt;&lt; movedDist &lt;&lt; &#39; &#39; &lt;&lt; stoneCnt &lt;&lt; &#39; &#39; &lt;&lt; attackCnt &lt;&lt; &#39;\n&#39;;
    }
    cout &lt;&lt; 0;

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2025_하_P_1_L13]]></title>
            <link>https://velog.io/@dev_noob8933/2025%ED%95%98P1L13</link>
            <guid>https://velog.io/@dev_noob8933/2025%ED%95%98P1L13</guid>
            <pubDate>Sat, 11 Apr 2026 01:37:50 GMT</pubDate>
            <description><![CDATA[<h1 id="ai-로봇청소기">AI 로봇청소기</h1>
<h2 id="bfs-simulation">BFS, Simulation</h2>
<p>평균 : 180&#39;</p>
<hr>
<p>sol : 90&#39; 18&#39;&#39;</p>
<ul>
<li>수행 시간 : 8ms</li>
<li>메모리 : 0MB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;utility&gt;
#include &lt;tuple&gt;
#include &lt;queue&gt;
#include &lt;climits&gt;
using namespace std;

// 1-base
#define MAX_N 31
#define EMPTY 0
#define BLOCK -1

int n, k, l;
int dustGrid[MAX_N][MAX_N];
int robotGrid[MAX_N][MAX_N];

// 우, 하, 좌, 상
const int ds[4][2] = {
    {0, 1},
    {1, 0},
    {0, -1},
    {-1, 0}
};

struct Robot {
    int i;
    int j;

    Robot() {}
    Robot(int _i, int _j) :
        i(_i), j(_j) { }
};

vector&lt;Robot&gt; robots;

void DebugDG() {
    cout &lt;&lt; endl &lt;&lt; &quot;DEBUG DUST GRID&quot; &lt;&lt; endl;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (dustGrid[i][j] == BLOCK) cout &lt;&lt; &quot;B &quot;;
            else cout &lt;&lt; dustGrid[i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;DEBUG FIN&quot; &lt;&lt; endl &lt;&lt; endl;
}

void DebugRG() {
    cout &lt;&lt; endl &lt;&lt; &quot;DEBUG ROBOT GRID&quot; &lt;&lt; endl;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (dustGrid[i][j] == BLOCK) cout &lt;&lt; &quot;B &quot;;
            else cout &lt;&lt; robotGrid[i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;DEBUG FIN&quot; &lt;&lt; endl &lt;&lt; endl;
}

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= n &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= n;
}

void Init() {
    cin &gt;&gt; n &gt;&gt; k &gt;&gt; l;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            cin &gt;&gt; dustGrid[i][j];
        }
    }

    robots.resize(k + 1);
    for (int i = 1; i &lt;= k; i++) {
        int r, c;
        cin &gt;&gt; r &gt;&gt; c;
        robots[i] = Robot(r, c);
        robotGrid[r][c] = i;
    }
}

pair&lt;int, int&gt; GetNextPos(int i, int j) {
    if (dustGrid[i][j] &gt; 0) return make_pair(i, j);

    // 거리, 행, 열
    tuple&lt;int, int, int&gt; bestPos = {INT_MAX, INT_MAX, INT_MAX};
    bool visited[MAX_N][MAX_N];
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            visited[i][j] = false;
        }
    }
    // 거리, 행, 열
    queue&lt;tuple&lt;int, int, int&gt;&gt; q;
    q.push({ 0, i, j });
    visited[i][j] = true;

    while (!q.empty()) {
        int cd, ci, cj;
        tie(cd, ci, cj) = q.front();
        q.pop();

        int bd;
        tie(bd, ignore, ignore) = bestPos;
        if (bd &lt; cd) continue;

        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + ds[d][0], nj = cj + ds[d][1];

            if (!InGrid(ni, nj)) continue;
            if (visited[ni][nj]) continue;
            if (dustGrid[ni][nj] == BLOCK) continue;
            if (robotGrid[ni][nj] != EMPTY) continue;

            visited[ni][nj] = true;
            if (dustGrid[ni][nj] == EMPTY) q.push({ cd + 1, ni, nj });
            else if (dustGrid[ni][nj] &gt; 0) {
                tuple&lt;int, int, int&gt; curPos = make_tuple(cd + 1, ni, nj);
                if (curPos &lt; bestPos) bestPos = curPos;
            }
        }
    }

    int best_i, best_j;
    tie(ignore, best_i, best_j) = bestPos;
    return make_pair(best_i, best_j);
}

void RobotsMove() {
    for (int r = 1; r &lt;= k; r++) {
        int ci = robots[r].i, cj = robots[r].j;
        pair&lt;int, int&gt; nextPos = GetNextPos(ci, cj);

        if (nextPos.first == INT_MAX) continue;

        int ni = nextPos.first, nj = nextPos.second;
        robotGrid[ci][cj] = EMPTY;
        robotGrid[ni][nj] = r;

        robots[r].i = ni, robots[r].j = nj;
    }
}

void Clean() {
    for (int r = 1; r &lt;= k; r++) {
        int maxDust = 0, bestDir = -1;
        int ci = robots[r].i, cj = robots[r].j;
        dustGrid[ci][cj] = (dustGrid[ci][cj] &gt; 20 ? dustGrid[ci][cj] - 20 : EMPTY);

        for (int d = 0; d &lt; 4; d++) {
            int curDust = 0;
            for (int cd = 0; cd &lt; 4; cd++) {
                if (cd == (d + 2) % 4) continue;

                int ni = ci + ds[cd][0], nj = cj + ds[cd][1];
                if (!InGrid(ni, nj)) continue;
                if (dustGrid[ni][nj] == BLOCK) continue;

                // 최대 20까지만 청소 가능
                curDust += (dustGrid[ni][nj] &gt; 20 ? 20 : dustGrid[ni][nj]);
            }
            if (curDust &gt; maxDust) {
                maxDust = curDust;
                bestDir = d;
            }
        }

        if (bestDir == -1) continue;

        for (int d = 0; d &lt; 4; d++) {
            if (d == (bestDir + 2) % 4) continue;

            int ni = ci + ds[d][0], nj = cj + ds[d][1];
            if (!InGrid(ni, nj)) continue;
            if (dustGrid[ni][nj] == BLOCK) continue;

            dustGrid[ni][nj] = (dustGrid[ni][nj] &gt; 20 ? dustGrid[ni][nj] - 20 : EMPTY);
        }
    }
}

void IncreaseDust() {
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (dustGrid[i][j] == EMPTY || dustGrid[i][j] == BLOCK) continue;
            dustGrid[i][j] += 5;
        }
    }
}

void Diffuse() {
    int temp[MAX_N][MAX_N];
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            temp[i][j] = 0;
        }
    }

    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (dustGrid[i][j] != EMPTY) continue;

            int curDust = 0;
            for (int d = 0; d &lt; 4; d++) {
                int ni = i + ds[d][0], nj = j + ds[d][1];

                if (!InGrid(ni, nj)) continue;
                if (dustGrid[ni][nj] == BLOCK) continue;

                curDust += dustGrid[ni][nj];
            }

            curDust /= 10;
            temp[i][j] = curDust;
        }
    }

    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            dustGrid[i][j] += temp[i][j];
        }
    }
}

void PrintDust() {
    int total = 0;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (dustGrid[i][j] == EMPTY || dustGrid[i][j] == BLOCK) continue;
            total += dustGrid[i][j];
        }
    }
    cout &lt;&lt; total &lt;&lt; &#39;\n&#39;;
}

int main() {
    cin.tie(0)-&gt;sync_with_stdio(0);
    Init();

    for (int turn = 1; turn &lt;= l; turn++) {

        RobotsMove();

        Clean();

        IncreaseDust();

        Diffuse();

        PrintDust();
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 20061번 : 모노미노도미노2]]></title>
            <link>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-20061%EB%B2%88-%EB%AA%A8%EB%85%B8%EB%AF%B8%EB%85%B8%EB%8F%84%EB%AF%B8%EB%85%B82</link>
            <guid>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-20061%EB%B2%88-%EB%AA%A8%EB%85%B8%EB%AF%B8%EB%85%B8%EB%8F%84%EB%AF%B8%EB%85%B82</guid>
            <pubDate>Fri, 10 Apr 2026 07:53:38 GMT</pubDate>
            <description><![CDATA[<p>sol : 101&#39; 09&#39;&#39;</p>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>회전한 채로 그리드를 계속 생각하는 방식이 흥미로웠다.</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;utility&gt;
using namespace std;

#define BLUE 0
#define GREEN 1

int n, score;
bool grids[2][6][4];
bool blocks[4][2][2] = {
    {},

    {{1, 0},
    {0, 0}},

    {{1, 1},
    {0, 0}},

    {{1, 0},
    {1, 0}}
};

void Debug(int zone) {
    if (zone == BLUE) cout &lt;&lt; endl &lt;&lt; &quot;BLUE DEBUG&quot; &lt;&lt; endl;
    else if (zone == GREEN) cout &lt;&lt; endl &lt;&lt; &quot;GREEN DEBUG&quot; &lt;&lt; endl;

    for (int i = 0; i &lt; 6; i++) {
        for (int j = 0; j &lt; 4; j++) {
            cout &lt;&lt; grids[zone][i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;DEBUG FIN&quot; &lt;&lt; endl;
}

bool InGrid(int i, int j) {
    return 0 &lt;= i &amp;&amp; i &lt; 6 &amp;&amp; 0 &lt;= j &amp;&amp; j &lt; 6;
}

bool Fit(vector&lt;pair&lt;int, int&gt;&gt;&amp; area, bool grid[6][4]) {
    for (int a = 0; a &lt; area.size(); a++) {
        int ci = area[a].first, cj = area[a].second;
        if (!InGrid(ci, cj) || grid[ci][cj]) return false;
    }
    return true;
}

bool RowFull(int zone, int i) {
    for (int j = 0; j &lt; 4; j++) {
        if (grids[zone][i][j] == false) {
            return false;
        }
    }
    return true;
}

void Downward(int zone, int i) {
    for (int ci = i - 1; ci &gt;= 0; ci--) {
        for (int cj = 0; cj &lt; 4; cj++) {
            if (!grids[zone][ci][cj]) continue;

            grids[zone][ci][cj] = false;
            grids[zone][ci + 1][cj] = true;
        }
    }
}

void PushDown(int zone) {
    for (int i = 5; i &gt; 0; i--) {
        for (int j = 0; j &lt; 4; j++) {
            grids[zone][i][j] = grids[zone][i - 1][j];
            grids[zone][i - 1][j] = false;
        }
    }
}

void Drop(int zone, int type, int x, int y) {
    vector&lt;pair&lt;int, int&gt;&gt; curArea;
    for (int i = 0; i &lt; 2; i++) {
        for (int j = 0; j &lt; 2; j++) {
            if (!blocks[type][i][j]) continue;

            int ci = x + i, cj = y + j;
            curArea.push_back({ ci, cj });
        }
    }

    for (int a = 0; a &lt; curArea.size(); a++) {
        if (zone == BLUE) {
            int temp = curArea[a].first;
            curArea[a].first = curArea[a].second - y;
            curArea[a].second = 4 - temp - 1;
        }
        else {
            curArea[a].first -= x;
        }
    }

    while (true) {
        if (!Fit(curArea, grids[zone])) break;
        for (int a = 0; a &lt; curArea.size(); a++) {
            curArea[a].first++;
        }
    }

    for (int a = 0; a &lt; curArea.size(); a++) {
        grids[zone][--curArea[a].first][curArea[a].second] = true;
    }

    // 1. 만약 행이 가득 찼다면
    for (int i = 5; i &gt;= 0; i--) {
        bool full = RowFull(zone, i);

        while (full) {
            score++;
            for (int j = 0; j &lt; 4; j++) {
                grids[zone][i][j] = false;
            }
            Downward(zone, i);
            full = RowFull(zone, i);
        }
    }

    // 2. 연한 칸에 존재한다면
    int downNum = 0;
    for (int j = 0; j &lt; 4; j++) {
        if (grids[zone][0][j]) {
            downNum = 2;
            break;
        }
    }
    if (downNum == 0) {
        for (int j = 0; j &lt; 4; j++) {
            if (grids[zone][1][j]) {
                downNum = 1;
                break;
            }
        }
    }

    for (int d = 0; d &lt; downNum; d++) PushDown(zone);
}

void PrintBlockNum() {
    int cnt = 0;
    for (int zone = 0; zone &lt; 2; zone++) {
        for (int i = 0; i &lt; 6; i++) {
            for (int j = 0; j &lt; 4; j++) {
                if (grids[zone][i][j]) cnt++;
            }
        }
    }

    cout &lt;&lt; cnt;
}

int main() {
    cin.tie(0)-&gt;sync_with_stdio(0);
    cin &gt;&gt; n;

    for (int turn = 1; turn &lt;= n; turn++) {
        int t, x, y;
        cin &gt;&gt; t &gt;&gt; x &gt;&gt; y;

        Drop(GREEN, t, x, y);

        Drop(BLUE, t, x, y);
    }

    cout &lt;&lt; score &lt;&lt; &#39;\n&#39;;
    PrintBlockNum();

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2025_하_A_1_L12]]></title>
            <link>https://velog.io/@dev_noob8933/2025%ED%95%98A1L12</link>
            <guid>https://velog.io/@dev_noob8933/2025%ED%95%98A1L12</guid>
            <pubDate>Fri, 10 Apr 2026 04:42:33 GMT</pubDate>
            <description><![CDATA[<h1 id="택배-상하차">택배 상하차</h1>
<h2 id="simulation">Simulation</h2>
<p>평균 : 180&#39;</p>
<hr>
<p>sol : 165&#39; 24&#39;&#39;</p>
<ul>
<li>수행 시간 : 149ms -&gt; 10ms</li>
<li>메모리 : 0MB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>직사각형이어서 좌표를 전부 가지고 다닐 필요가 없었다.</li>
</ul>
<h3 id="after-revise">After revise</h3>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;utility&gt;
#include &lt;climits&gt;
#include &lt;algorithm&gt;
using namespace std;

// 1-based
#define MAX_N 51
#define EMPTY 0
#define MAX_K 100

int n, m;
int grid[MAX_N][MAX_N];
int curTarget;

struct Post {
    int h;
    int w;
    int r;
    int c;
};

vector&lt;Post&gt; posts;

void Debug() {
    cout &lt;&lt; endl &lt;&lt; &quot;DEBUG&quot; &lt;&lt; endl;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            cout &lt;&lt; grid[i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;DEBUG FIN&quot; &lt;&lt; endl;
}

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= n &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= n;
}

bool Fit(Post p) {
    for (int i = p.r; i &lt; p.r + p.h; i++) {
        for (int j = p.c; j &lt; p.c + p.w; j++) {
            if (!InGrid(i, j) || grid[i][j] != EMPTY) return false;
        }
    }
    return true;
}

void Drop() {
    posts.resize(MAX_K + 1);

    for (int p = 1; p &lt;= m; p++) {
        int k, h, w, c;
        cin &gt;&gt; k &gt;&gt; h &gt;&gt; w &gt;&gt; c;

        Post cur = { h, w, 1, c };

        while (true) {
            cur.r++;
            if (!Fit(cur)) break;
        }
        cur.r--;

        posts[k] = cur;

        for (int i = cur.r; i &lt; cur.r + cur.h; i++) {
            for (int j = cur.c; j &lt; cur.c + cur.w; j++) {
                grid[i][j] = k;
            }
        }
    }
}

bool Remained() {
    for (int j = 1; j &lt;= n; j++) {
        if (grid[n][j] != EMPTY) return true;
    }
    return false;
}

bool CanLeftPick(int id) {
    Post p = posts[id];

    int cur_c = p.c;

    while (cur_c &gt; 1) {
        cur_c--;

        for (int i = p.r; i &lt; p.r + p.h; i++) {
            if (grid[i][cur_c] != EMPTY) return false;
        }
    }

    return true;
}

bool CanRightPick(int id) {
    Post p = posts[id];

    int cur_c = p.c + p.w - 1;

    while (cur_c &lt; n) {
        cur_c++;

        for (int i = p.r; i &lt; p.r + p.h; i++) {
            if (grid[i][cur_c] != EMPTY) return false;
        }
    }

    return true;
}

void LeftPick() {
    int target = INT_MAX;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (grid[i][j] == EMPTY) continue;
            if (grid[i][j] == target) break;

            if (CanLeftPick(grid[i][j])) {
                if (grid[i][j] &lt; target) target = grid[i][j];
            }
            break;
        }
    }

    curTarget = target;
}

void RightPick() {
    int target = INT_MAX;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = n; j &gt;= 1; j--) {
            if (grid[i][j] == EMPTY) continue;
            if (grid[i][j] == target) break;

            if (CanRightPick(grid[i][j])) {
                if (grid[i][j] &lt; target) target = grid[i][j];
            }
            break;
        }
    }

    curTarget = target;
}

void Remove() {
    Post p = posts[curTarget];

    for (int i = p.r; i &lt; p.r + p.h; i++) {
        for (int j = p.c; j &lt; p.c + p.w; j++) {
            grid[i][j] = EMPTY;
        }
    }
}

void Fall(int id) {
    Post p = posts[id];

    int cur_r = p.r;
    bool moved = false;

    while (true) {
        int next_r = cur_r + 1;

        if (next_r + p.h - 1 &gt; n) break;

        bool can = true;
        for (int j = p.c; j &lt; p.c + p.w; j++) {
            if (grid[next_r + p.h - 1][j] != EMPTY) {
                can = false;
                break;
            }
        }

        if (!can) break;

        cur_r++;
        moved = true;
    }

    if (!moved) return;

    // 기존 위치 제거
    for (int i = p.r; i &lt; p.r + p.h; i++) {
        for (int j = p.c; j &lt; p.c + p.w; j++) {
            grid[i][j] = EMPTY;
        }
    }

    // 위치 갱신
    posts[id].r = cur_r;

    // 다시 배치
    for (int i = cur_r; i &lt; cur_r + p.h; i++) {
        for (int j = p.c; j &lt; p.c + p.w; j++) {
            grid[i][j] = id;
        }
    }
}

void FallPhase() {
    for (int i = n - 1; i &gt;= 1; i--) {
        for (int j = 1; j &lt;= n; j++) {
            if (grid[i][j] == EMPTY) continue;
            Fall(grid[i][j]);
        }
    }
}

int main() {
    cin.tie(0)-&gt;sync_with_stdio(0);
    cin &gt;&gt; n &gt;&gt; m;

    Drop();

    while (Remained()) {
        LeftPick();
        cout &lt;&lt; curTarget &lt;&lt; &#39;\n&#39;;

        Remove();

        FallPhase();

        RightPick();
        cout &lt;&lt; curTarget &lt;&lt; &#39;\n&#39;;

        Remove();

        FallPhase();
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 17837번 : 새로운 게임2]]></title>
            <link>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-17837%EB%B2%88-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B2%8C%EC%9E%842</link>
            <guid>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-17837%EB%B2%88-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B2%8C%EC%9E%842</guid>
            <pubDate>Thu, 09 Apr 2026 02:44:04 GMT</pubDate>
            <description><![CDATA[<p>sol : 81&#39; 40&#39;&#39;</p>
<ul>
<li>수행 시간 : 4ms -&gt; 0ms</li>
<li>메모리 : 2032KB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>다음엔 1시간 이내로 풀어보자.</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

#define MAX_N 13
#define WHITE 0
#define RED 1
#define BLUE 2

#define RIGHT 1
#define LEFT 2
#define UP 3
#define DOWN 4

int n, k, turn;
bool impossible, finish;
// 우, 좌, 상, 하
const int ds[5][2] = {
    {0, 0},
    {0, 1},
    {0, -1},
    {-1, 0},
    {1, 0}
};

struct Horse {
    int i;
    int j;
    int dir;

    Horse() {}
    Horse(int _i, int _j, int _dir) :
        i(_i), j(_j), dir(_dir) { }
};

vector&lt;int&gt; horseGrid[MAX_N][MAX_N];
int boardGrid[MAX_N][MAX_N];
vector&lt;Horse&gt; horses;
vector&lt;bool&gt; moved;

void Init() {
    cin &gt;&gt; n &gt;&gt; k;

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

    horses.resize(k + 1);
    for (int i = 1; i &lt;= k; i++) {
        int r, c, dir;
        cin &gt;&gt; r &gt;&gt; c &gt;&gt; dir;
        horseGrid[r][c].push_back(i);
        horses[i] = Horse(r, c, dir);
    }

    moved.resize(k + 1);
}

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= n &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= n;
}

int Oppos(int dir) {
    if (dir == LEFT) return RIGHT;
    if (dir == RIGHT) return LEFT;
    if (dir == UP) return DOWN;
    if (dir == DOWN) return UP;
    return -1;
}

void Move() {
    for (int h = 1; h &lt;= k; h++) {
        moved[h] = false;

        int ci = horses[h].i, cj = horses[h].j, cd = horses[h].dir;
        int ni = ci + ds[cd][0], nj = cj + ds[cd][1];

        // 체스판을 벗어나거나 파란색인 경우
        if (!InGrid(ni, nj) || boardGrid[ni][nj] == BLUE) {
            cd = Oppos(cd);
            ni = ci + ds[cd][0], nj = cj + ds[cd][1];
            horses[h].dir = cd;
            if (!InGrid(ni, nj) || boardGrid[ni][nj] == BLUE) continue;
        }

        vector&lt;int&gt; stayer, mover;
        for (int i = 0; i &lt; horseGrid[ci][cj].size(); i++) {
            if (horseGrid[ci][cj][i] != h) {
                stayer.push_back(horseGrid[ci][cj][i]);
            }
            else {
                for (int j = i; j &lt; horseGrid[ci][cj].size(); j++) {
                    mover.push_back(horseGrid[ci][cj][j]);
                }
                break;
            }
        }
        horseGrid[ci][cj] = stayer;

        // 흰색인 경우
        if (boardGrid[ni][nj] == WHITE) {
            for (int i = 0; i &lt; mover.size(); i++) {
                horseGrid[ni][nj].push_back(mover[i]);
                horses[mover[i]].i = ni, horses[mover[i]].j = nj;
            }
        }

        // 빨간색인 경우
        else if (boardGrid[ni][nj] == RED) {
            for (int i = mover.size() - 1; i &gt;= 0; i--) {
                horseGrid[ni][nj].push_back(mover[i]);
                horses[mover[i]].i = ni, horses[mover[i]].j = nj;
            }
        }

        moved[h] = true;
        if (horseGrid[ni][nj].size() &gt;= 4) {
            finish = true;
            return;
        }
    }
}

bool Impossible() {
    impossible = true;
    for (int i = 1; i &lt;= k; i++) {
        if (moved[i]) impossible = false;
    }

    return impossible;
}

int main() {
    Init();

    while (++turn &lt;= 1000) {
        Move();

        if (finish) {
            cout &lt;&lt; turn;
            return 0;
        }

        if (Impossible()) break;
    }

    cout &lt;&lt; -1;

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 5373번 : 큐빙]]></title>
            <link>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-5373%EB%B2%88-%ED%81%90%EB%B9%99</link>
            <guid>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-5373%EB%B2%88-%ED%81%90%EB%B9%99</guid>
            <pubDate>Wed, 08 Apr 2026 09:03:49 GMT</pubDate>
            <description><![CDATA[<p>sol : 138&#39; 33&#39;&#39;</p>
<ul>
<li>수행 시간 : 4ms</li>
<li>메모리 : 2168KB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>깔끔하게 전부 정리하고 풀면 1시간 이내도 가능했을 것.</li>
<li>암산 절대금지</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
using namespace std;

#define U 0
#define D 1
#define F 2
#define B 3
#define L 4
#define R 5

#define CW 0
#define CCW 1

int T;

struct Order {
    int face;
    int dir;
};

vector&lt;Order&gt; orders;

char colors[6] = { &#39;w&#39;, &#39;y&#39;, &#39;r&#39;, &#39;o&#39;, &#39;g&#39;, &#39;b&#39; };
char cube[6][3][3];

int Translator(char order) {
    if (order == &#39;U&#39;) return U;
    if (order == &#39;D&#39;) return D;
    if (order == &#39;F&#39;) return F;
    if (order == &#39;B&#39;) return B;
    if (order == &#39;L&#39;) return L;
    if (order == &#39;R&#39;) return R;
    if (order == &#39;+&#39;) return CW;
    if (order == &#39;-&#39;) return CCW;
}

void RotateOutter(int face, int dir) {
    char tempFace[3][3];

    // 시계 회전 
    if (dir == CW) {
        for (int i = 0; i &lt; 3; i++) {
            for (int j = 0; j &lt; 3; j++) {
                tempFace[j][2 - i] = cube[face][i][j];
            }
        }
    }
    // 반시계 회전
    else if (dir == CCW) {
        for (int i = 0; i &lt; 3; i++) {
            for (int j = 0; j &lt; 3; j++) {
                tempFace[2 - j][i] = cube[face][i][j];
            }
        }
    }

    for (int i = 0; i &lt; 3; i++) {
        for (int j = 0; j &lt; 3; j++) {
            cube[face][i][j] = tempFace[i][j];
        }
    }
}

void Rotater(int face, int dir) {
    RotateOutter(face, dir);

    if (face == U) {
        char temp[3] = { cube[R][0][0], cube[R][0][1], cube[R][0][2] };
        if (dir == CW) {    
            for (int x = 0; x &lt; 3; x++) {
                cube[R][0][x] = cube[B][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][0][x] = cube[L][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][0][x] = cube[F][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][0][x] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[R][0][x] = cube[F][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][0][x] = cube[L][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][0][x] = cube[B][0][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][0][x] = temp[x];
            }
        }
    }

    else if (face == D) {
        char temp[3] = { cube[R][2][0], cube[R][2][1], cube[R][2][2] };
        if (dir == CW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[R][2][x] = cube[F][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][2][x] = cube[L][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2][x] = cube[B][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2][x] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[R][2][x] = cube[B][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2][x] = cube[L][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2][x] = cube[F][2][x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][2][x] = temp[x];
            }
        }
    }

    else if (face == F) {
        char temp[3] = { cube[U][2][0], cube[U][2][1], cube[U][2][2] };
        if (dir == CW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][2][x] = cube[L][2 - x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2 - x][2] = cube[D][0][2 - x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][0][2 - x] = cube[R][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[R][x][0] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][2][x] = cube[R][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[R][x][0] = cube[D][0][2 - x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][0][2 - x] = cube[L][2 - x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2 - x][2] = temp[x];
            }
        }
    }

    else if (face == B) {
        char temp[3] = { cube[U][0][0], cube[U][0][1], cube[U][0][2] };
        if (dir == CW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][0][x] = cube[R][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[R][x][2] = cube[D][2][2 - x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][2][2 - x] = cube[L][2 - x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2 - x][0] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][0][x] = cube[L][2 - x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[L][2 - x][0] = cube[D][2][2 - x];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][2][2 - x] = cube[R][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[R][x][2] = temp[x];
            }
        }
    }

    else if (face == L) {
        char temp[3] = { cube[U][0][0], cube[U][1][0], cube[U][2][0] };
        if (dir == CW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][x][0] = cube[B][2 - x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2 - x][2] = cube[D][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][x][0] = cube[F][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][x][0] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][x][0] = cube[F][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][x][0] = cube[D][x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][x][0] = cube[B][2 - x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2 - x][2] = temp[x];
            }
        }
    }

    else if (face == R) {
        char temp[3] = { cube[U][0][2], cube[U][1][2], cube[U][2][2] };
        if (dir == CW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][x][2] = cube[F][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][x][2] = cube[D][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][x][2] = cube[B][2 - x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2 - x][0] = temp[x];
            }
        }
        else if (dir == CCW) {
            for (int x = 0; x &lt; 3; x++) {
                cube[U][x][2] = cube[B][2 - x][0];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[B][2 - x][0] = cube[D][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[D][x][2] = cube[F][x][2];
            }
            for (int x = 0; x &lt; 3; x++) {
                cube[F][x][2] = temp[x];
            }
        }
    }
}

void Init() {
    int r;
    cin &gt;&gt; r;
    orders.resize(r);

    for (int f = 0; f &lt; 6; f++) {
        for (int i = 0; i &lt; 3; i++) {
            for (int j = 0; j &lt; 3; j++) {
                cube[f][i][j] = colors[f];
            }
        }
    }

    for (int i = 0; i &lt; r; i++) {
        string order;
        cin &gt;&gt; order;


        orders[i].face = Translator(order[0]);
        orders[i].dir = Translator(order[1]);
    }
}

void PrintUpFace() {
    for (int i = 0; i &lt; 3; i++) {
        for (int j = 0; j &lt; 3; j++) {
            cout &lt;&lt; cube[U][i][j];
        }
        cout &lt;&lt; &#39;\n&#39;;
    }
}

int main() {
    cin.tie(0)-&gt;sync_with_stdio(0);

    cin &gt;&gt; T;
    for (int t = 1; t &lt;= T; t++) {
        Init();

        for (int o = 0; o &lt; orders.size(); o++) {
            Rotater(orders[o].face, orders[o].dir);
        }

        PrintUpFace();
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2023_하_P_1_L14 복습]]></title>
            <link>https://velog.io/@dev_noob8933/2023%ED%95%98P1L14-%EB%B3%B5%EC%8A%B5</link>
            <guid>https://velog.io/@dev_noob8933/2023%ED%95%98P1L14-%EB%B3%B5%EC%8A%B5</guid>
            <pubDate>Wed, 08 Apr 2026 06:25:59 GMT</pubDate>
            <description><![CDATA[<h1 id="루돌프의-반란">루돌프의 반란</h1>
<h2 id="simulation">Simulation</h2>
<p>평균 : 180&#39;</p>
<hr>
<p>sol : 108&#39; 5&#39;&#39;</p>
<ul>
<li>수행 시간 : 7ms</li>
<li>메모리 : 0MB</li>
</ul>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>처음 풀었을 때는 9ms였는데 이번에 7ms가 나왔다.</li>
<li>상태관리를 적재적소에서 잘한 것 같다.</li>
<li>재귀가 약점이었는데 상태 관리를 위한 분기를 명확하게 처리했다.</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;climits&gt;
#include &lt;algorithm&gt;
using namespace std;

// 1-based
const int MAX_N = 51;
const int RUDOLP = -1;
const int EMPTY = 0;
int n, m, p, c, d;
int turn;
const int ds[8][2] = {
    {-1, 0},
    {0, 1},
    {1, 0},
    {0, -1},
    {-1, -1},
    {-1, 1},
    {1, -1},
    {1, 1}
};

struct Santa {
    int r;
    int c;
    int stun;
    bool onGrid;
    int score;

    Santa() { }
};

struct Rudolp {
    int r;
    int c;

    Rudolp() :
        r(-1), c(-1) { }
};

vector&lt;Santa&gt; santas;
Rudolp rudolp;
int grid[MAX_N][MAX_N];

void Debug() {
    cout &lt;&lt; endl &lt;&lt; &quot;DEBUG&quot; &lt;&lt; endl;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= n; j++) {
            if (grid[i][j] == RUDOLP) cout &lt;&lt; &#39;R&#39; &lt;&lt; &#39; &#39;;
            else cout &lt;&lt; grid[i][j] &lt;&lt; &#39; &#39;;
        }
        cout &lt;&lt; endl;
    }
    cout &lt;&lt; &quot;DEBUG FIN&quot; &lt;&lt; endl &lt;&lt; endl;
}

void Init() {
    cin &gt;&gt; n &gt;&gt; m &gt;&gt; p &gt;&gt; c &gt;&gt; d;

    cin &gt;&gt; rudolp.r &gt;&gt; rudolp.c;
    grid[rudolp.r][rudolp.c] = RUDOLP;

    santas.resize(p + 1);
    for (int i = 1; i &lt;= p; i++) {
        int id, r, c;
        cin &gt;&gt; id &gt;&gt; r &gt;&gt; c;
        santas[id].r = r, santas[id].c = c, santas[id].score = 0;
        santas[id].onGrid = true, santas[id].stun = 0;

        grid[r][c] = id;
    }
}

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= n &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= n;
}

int Dist(int r1, int c1, int r2, int c2) {
    return (r1 - r2) * (r1 - r2) + (c1 - c2) * (c1 - c2);
}

void Correlation(int id, int dir) {
    int ni = santas[id].r + ds[dir][0];
    int nj = santas[id].c + ds[dir][1];

    if (!InGrid(ni, nj)) {
        santas[id].onGrid = false;
        return;
    }

    santas[id].r = ni, santas[id].c = nj;

    if (grid[ni][nj] &gt; 0) {
        int nextId = grid[ni][nj];
        grid[ni][nj] = id;
        Correlation(nextId, dir);
        return;
    }

    if (grid[ni][nj] == EMPTY) {
        grid[ni][nj] = id;
        return;
    }
}

void CollideRtoS(int id, int dir) {
    santas[id].score += c;
    santas[id].stun = turn + 2;

    int ni = santas[id].r + ds[dir][0] * c;
    int nj = santas[id].c + ds[dir][1] * c;

    if (!InGrid(ni, nj)) {
        santas[id].onGrid = false;
        return;
    }

    santas[id].r = ni, santas[id].c = nj;

    if (grid[ni][nj] &gt; 0) {
        int nextId = grid[ni][nj];
        grid[ni][nj] = id;
        Correlation(nextId, dir);
        return;
    }

    if (grid[ni][nj] == EMPTY) {
        grid[ni][nj] = id;
        return;
    }
}

void RudolpMove() {
    int minDist = INT_MAX;
    // r-max, c-max, id
    vector&lt;tuple&lt;int, int, int&gt;&gt; targets;
    for (int s = 1; s &lt;= p; s++) {
        if (!santas[s].onGrid) continue;

        int curDist = Dist(rudolp.r, rudolp.c, santas[s].r, santas[s].c);
        if (curDist &lt; minDist) {
            minDist = curDist;
            targets.clear();
            targets.push_back(make_tuple(-santas[s].r, -santas[s].c, s));
        }
        else if (curDist == minDist) {
            targets.push_back(make_tuple(-santas[s].r, -santas[s].c, s));
        }
    }
    sort(targets.begin(), targets.end());

    int bestDist = INT_MAX, best_ni = -1, best_nj = -1, best_dir = -1;
    int ti, tj, tid;
    tie(ti, tj, tid) = targets[0];
    ti *= -1, tj *= -1;

    for (int d = 0; d &lt; 8; d++) {
        int ni = rudolp.r + ds[d][0], nj = rudolp.c + ds[d][1];
        if (!InGrid(ni, nj)) continue;

        int curDist = Dist(ni, nj, ti, tj);
        if (curDist &lt; bestDist) {
            bestDist = curDist;
            best_ni = ni, best_nj = nj;
            best_dir = d;
        }
    }

    grid[rudolp.r][rudolp.c] = EMPTY;
    rudolp.r = best_ni, rudolp.c = best_nj;
    if (grid[rudolp.r][rudolp.c] != EMPTY) {
        int nextId = grid[rudolp.r][rudolp.c];
        grid[rudolp.r][rudolp.c] = RUDOLP;
        CollideRtoS(nextId, best_dir);
    }
    else {
        grid[rudolp.r][rudolp.c] = RUDOLP;
    }
}

void CollideStoR(int id, int dir) {
    santas[id].score += d;
    santas[id].stun = turn + 2;
    int opposDir = (dir + 2) % 4;

    int ni = santas[id].r + ds[opposDir][0] * d;
    int nj = santas[id].c + ds[opposDir][1] * d;

    if (!InGrid(ni, nj)) {
        santas[id].onGrid = false;
        return;
    }

    santas[id].r = ni, santas[id].c = nj;
    if (grid[ni][nj] &gt; 0) {
        int nextId = grid[ni][nj];
        grid[ni][nj] = id;
        Correlation(nextId, opposDir);
        return;
    }

    if (grid[ni][nj] == EMPTY) {
        grid[ni][nj] = id;
        return;
    }
}

void SantasMove() {
    for (int s = 1; s &lt;= p; s++) {
        // 탈락한 산타는 움직이지 않음.
        if (!santas[s].onGrid) continue;
        // k턴에서 기절한 산타는 k+1턴까지는 움직이지 않음.
        if (santas[s].stun &gt; turn) continue;

        int ci = santas[s].r, cj = santas[s].c;
        int bestDist = Dist(rudolp.r, rudolp.c, ci, cj);
        int best_i = -1, best_j = -1, best_dir = -1;

        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + ds[d][0], nj = cj + ds[d][1];

            if (!InGrid(ni, nj)) continue;
            if (grid[ni][nj] &gt; 0) continue;
            int curDist = Dist(rudolp.r, rudolp.c, ni, nj);
            if (curDist &lt; bestDist) {
                bestDist = curDist;
                best_i = ni, best_j = nj, best_dir = d;
            }
        }
        // 가까워질 수 있는 방법이 없다면 움직이지 않는다.
        if (best_i == -1) continue;

        grid[ci][cj] = EMPTY;
        santas[s].r = best_i, santas[s].c = best_j;
        if (grid[santas[s].r][santas[s].c] == RUDOLP) {
            CollideStoR(s, best_dir);
        }
        else {
            grid[santas[s].r][santas[s].c] = s;
        }
    }
}

bool Finish() {
    for (int s = 1; s &lt;= p; s++) {
        if (santas[s].onGrid) return false;
    }
    return true;
}

void PrintScore() {
    for (int s = 1; s &lt;= p; s++) {
        cout &lt;&lt; santas[s].score &lt;&lt; &#39; &#39;;
    }
    cout &lt;&lt; endl;
}

void Salary() {
    for (int s = 1; s &lt;= p; s++) {
        if (!santas[s].onGrid) continue;
        santas[s].score++;
    }
}

int main() {
    Init();

    while(++turn &lt;= m) {
        RudolpMove();

        SantasMove();

        if (Finish()) break;

        Salary();
    }

    PrintScore();

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[백준 23288번 : 주사위 굴리기2]]></title>
            <link>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-23288%EB%B2%88-%EC%A3%BC%EC%82%AC%EC%9C%84-%EA%B5%B4%EB%A6%AC%EA%B8%B02</link>
            <guid>https://velog.io/@dev_noob8933/%EB%B0%B1%EC%A4%80-23288%EB%B2%88-%EC%A3%BC%EC%82%AC%EC%9C%84-%EA%B5%B4%EB%A6%AC%EA%B8%B02</guid>
            <pubDate>Tue, 07 Apr 2026 11:01:37 GMT</pubDate>
            <description><![CDATA[<p>sol : 38&#39; 28&#39;&#39;</p>
<ul>
<li>수행 시간 : 8ms</li>
<li>메모리 : 2028KB</li>
</ul>
<hr>
<blockquote>
<h3 id="learnings">Learnings</h3>
</blockquote>
<ul>
<li>전처리를 하면 좋은 문제였다.</li>
<li>실전에서도 이렇게 전처리 생각이 먼저 떠오르려나.<ul>
<li>미지의 영역 탈출도 같은 논리였다.</li>
</ul>
</li>
<li>전처리를 하고 수행하니까 8ms에서 0ms로 줄었다.   </li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include &lt;queue&gt;
#include &lt;utility&gt;
#include &lt;vector&gt;

using namespace std;
// 1-based
#define MAX_N 21
#define MAX_M 21

#define EAST 0
#define SOUTH 1
#define WEST 2
#define NORTH 3

int n, m, k;
int score;
int scoreBoard[MAX_N][MAX_M];
bool visited[MAX_N][MAX_M];
int preprocessedBoard[MAX_N][MAX_M];

struct Dice {
    int r;
    int c;
    int dir;
    int top;
    int front;
    int right;

    Dice() :
        r(1), c(1), dir(EAST), top(1), front(5), right(3) {
    }
};
Dice dice;

// 동, 남, 서, 북
const int ds[4][2] = {
    {0,1},
    {1,0},
    {0,-1},
    {-1,0}
};

void Init() {
    cin &gt;&gt; n &gt;&gt; m &gt;&gt; k;
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= m; j++) {
            cin &gt;&gt; scoreBoard[i][j];
        }
    }
}

bool InGrid(int i, int j) {
    return 1 &lt;= i &amp;&amp; i &lt;= n &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= m;
}

void ScoreBFS(int i, int j) {
    int curNum = scoreBoard[i][j];
    queue&lt;pair&lt;int, int&gt;&gt; q;
    vector&lt;pair&lt;int, int&gt;&gt; curArea;

    q.push({ i, j });
    curArea.push_back({ i, j });
    visited[i][j] = true;

    while (!q.empty()) {
        int ci = q.front().first, cj = q.front().second;
        q.pop();

        for (int d = 0; d &lt; 4; d++) {
            int ni = ci + ds[d][0], nj = cj + ds[d][1];

            if (!InGrid(ni, nj) || visited[ni][nj] || scoreBoard[ni][nj] != curNum) continue;

            visited[ni][nj] = true;
            q.push({ ni, nj });
            curArea.push_back({ ni, nj });
        }
    }

    for (int i = 0; i &lt; curArea.size(); i++) {
        preprocessedBoard[curArea[i].first][curArea[i].second] = curNum * curArea.size();
    }
}

void ScoreBoardPreprocess() {
    for (int i = 1; i &lt;= n; i++) {
        for (int j = 1; j &lt;= m; j++) {
            if (visited[i][j]) continue;
            ScoreBFS(i, j);
        }
    }
}

void Translator(int dir) {
    int ct = dice.top, cf = dice.front, cr = dice.right;
    if (dir == EAST) {
        dice.top = 7 - cr;
        dice.right = ct;
    }
    else if (dir == WEST) {
        dice.top = cr;
        dice.right = 7 - ct;
    }
    else if (dir == SOUTH) {
        dice.front = ct;
        dice.top = 7 - cf;
    }
    else if (dir == NORTH) {
        dice.front = 7 - ct;
        dice.top = cf;
    }
}

void Roll() {
    int cd = dice.dir;
    int ni = dice.r + ds[cd][0], nj = dice.c + ds[cd][1];
    if (!InGrid(ni, nj)) {
        cd = (cd + 2) % 4;
        ni = dice.r + ds[cd][0], nj = dice.c + ds[cd][1];

    }

    dice.r = ni, dice.c = nj, dice.dir = cd;
    Translator(cd);
}

void NextDir() {
    int A = 7 - dice.top, B = scoreBoard[dice.r][dice.c];
    if (A &gt; B) dice.dir = (dice.dir + 1 == 4) ? 0 : dice.dir + 1;
    else if (A &lt; B) dice.dir = (dice.dir - 1 == -1) ? 3 : dice.dir - 1;
}

int main() {
    Init();

    ScoreBoardPreprocess();

    for (int turn = 1; turn &lt;= k; turn++) {
        Roll();

        score += preprocessedBoard[dice.r][dice.c];

        NextDir();
    }

    cout &lt;&lt; score;

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