<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ghost_dog.log</title>
        <link>https://velog.io/</link>
        <description>한림대학교 정보과학대 2학년 재학중 / 육군 정보보호병 22-2기</description>
        <lastBuildDate>Sun, 15 Sep 2024 07:20:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ghost_dog.log</title>
            <url>https://velog.velcdn.com/images/ghost_dog/profile/552a692b-8afc-4161-93b4-048e27493535/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ghost_dog.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ghost_dog" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[의사결정트리 / 랜덤포레스트 / 로지스틱회귀를 이용한 유방암 사망률 예측 모듈 ]]></title>
            <link>https://velog.io/@ghost_dog/%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95%ED%8A%B8%EB%A6%AC-%EB%9E%9C%EB%8D%A4%ED%8F%AC%EB%A0%88%EC%8A%A4%ED%8A%B8-%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1%ED%9A%8C%EA%B7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9C%A0%EB%B0%A9%EC%95%94-%EC%82%AC%EB%A7%9D%EB%A5%A0-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@ghost_dog/%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95%ED%8A%B8%EB%A6%AC-%EB%9E%9C%EB%8D%A4%ED%8F%AC%EB%A0%88%EC%8A%A4%ED%8A%B8-%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1%ED%9A%8C%EA%B7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9C%A0%EB%B0%A9%EC%95%94-%EC%82%AC%EB%A7%9D%EB%A5%A0-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Sun, 15 Sep 2024 07:20:21 GMT</pubDate>
            <description><![CDATA[<p><a href="https://drive.google.com/file/d/1v22xguVj1U0bB0rPkoFBBaaabt7f_bBZ/view?usp=sharing">원본데이터 출처 : kaggle</a>
<img src="https://velog.velcdn.com/images/ghost_dog/post/7366020c-bd89-4eab-a589-44029f5db3d8/image.png" alt=""></p>
<pre><code>import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split, KFold, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from datetime import datetime
import warnings

%matplotlib inline 

def encode_features(dataDF):
    features = [&#39;Gender&#39;,&#39;Tumour_Stage&#39;,&#39;Histology&#39;]
    for feature in features:
        le = LabelEncoder()
        le = le.fit(dataDF[feature])
        dataDF[feature] = le.transform(dataDF[feature])
    return dataDF

def calc (date_str1, date_str2):
    date_format = &quot;%d-%b-%y&quot;

    date1 = datetime.strptime(date_str1, date_format)
    date2 = datetime.strptime(date_str2, date_format)

    delta = date2 - date1

    return delta.days

BRCA_df = pd.read_csv(r&quot;C:\Users\User\Data_Handling\BRCA.csv&quot;).head(333)
#경고무시 
warnings.simplefilter(action=&#39;ignore&#39;, category=FutureWarning)

#데이터 전처리
BRCA_df[&#39;Date_of_Surgery&#39;] = pd.to_datetime(BRCA_df[&#39;Date_of_Surgery&#39;], format=&#39;%d-%b-%y&#39;)
BRCA_df[&#39;Date_of_Last_Visit&#39;] = pd.to_datetime(BRCA_df[&#39;Date_of_Last_Visit&#39;],format=&#39;%d-%b-%y&#39;)
BRCA_df[&#39;Surgery_Visit_Between&#39;] = BRCA_df[&#39;Date_of_Last_Visit&#39;] - BRCA_df[&#39;Date_of_Surgery&#39;]
BRCA_df = BRCA_df[BRCA_df[&#39;Gender&#39;]!=&quot;MALE&quot;]
BRCA_df = BRCA_df.dropna()
encode_features(BRCA_df)
BRCA_df = BRCA_df.drop([&#39;Patient_ID&#39;,&#39;Surgery_type&#39;,&#39;Date_of_Last_Visit&#39;,&#39;Date_of_Surgery&#39;,&#39;Gender&#39;],axis=1)
BRCA_df[[&quot;ER status&quot;,&quot;PR status&quot;,&quot;HER2 status&quot;]] = 1 if &quot;Positive&quot; else 0
BRCA_df[&quot;Surgery_Visit_Between&quot;].fillna(BRCA_df[&quot;Surgery_Visit_Between&quot;].mean(),inplace=True)
BRCA_df[&quot;Surgery_Visit_Between&quot;] = BRCA_df[&quot;Surgery_Visit_Between&quot;].apply(lambda x : int(str(x).split()[0]))

#데이터 분류
Y_BRCA_df = BRCA_df[&quot;Patient_Status&quot;]
X_BRCA_df = BRCA_df.drop([&quot;Patient_Status&quot;],axis=1)

#머신러닝
dt_clf = DecisionTreeClassifier()
rf_clf = RandomForestClassifier()
lr_clf = LogisticRegression(solver=&#39;liblinear&#39;)

X_train,X_test,Y_train,Y_test = train_test_split(X_BRCA_df,Y_BRCA_df,test_size=0.2,random_state=11)


#의사결정트리 
print(&quot;👨🏻‍⚕️의사결정트리&quot;)
dt_clf.fit(X_train,Y_train)
dt_pred = dt_clf.predict(X_test)
print(&quot;정확도:&quot;,accuracy_score(Y_test,dt_pred))
scores = cross_val_score(dt_clf,X_BRCA_df,Y_BRCA_df,cv=5)
print(&quot;Starified_KFold 교차검증 정확도:&quot;,scores.mean())
print()

#랜덤포레스트 
print(&quot;🌳랜덤포레스트&quot;)
rf_clf.fit(X_train,Y_train)
rf_pred = rf_clf.predict(X_test)
print(&quot;정확도:&quot;,accuracy_score(Y_test,rf_pred))
scores = cross_val_score(rf_clf,X_BRCA_df,Y_BRCA_df,cv=5)
print(&quot;Starified_KFold 교차검증 정확도:&quot;,scores.mean())
print()

#로지스틱회귀 
print(&quot;💻로지스틱회귀&quot;)
lr_clf.fit(X_train,Y_train)
lr_pred = lr_clf.predict(X_test)
scores = cross_val_score(dt_clf,X_BRCA_df,Y_BRCA_df,cv=5)
print(&quot;Starified_KFold 교차검증 정확도:&quot;,scores.mean())
print(&quot;정확도:&quot;,accuracy_score(Y_test,lr_pred))
print()
</code></pre><p><img src="https://velog.velcdn.com/images/ghost_dog/post/3de91413-a604-45cc-a452-134a48a6e755/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.21 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.21-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-1a0hyggf</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.21-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-1a0hyggf</guid>
            <pubDate>Fri, 23 Feb 2024 06:05:39 GMT</pubDate>
            <description><![CDATA[<p>소마 구현쪽에 투포인터 나왔다는 소리가 있어 어제 하루종일 투포인터 공부하느라 게시글을 못올렸습니다 ㅜㅜ...
하지만 큰 수확이 있었으니...바로 골드4 투포인터 문제를 자력솔 했습니다!</p>
<blockquote>
<p><strong>1806번 - 부분합</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/918a9048-9b89-4efd-ab88-7de290959bb5/image.png" alt=""></p>
</blockquote>
<p>수열이 주어지면 S보다 크거나 같은 부분수열의 합 중에서 가장 크기가 작은 부분수열을 구하면 되는 문제입니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/753d2d39-d85c-4019-9103-efdecb73f198/image.JPG" alt="">
일단 위 문제에 나온 수열을 그대로 가져왔습니다. 여기서 투포인터     개념을 적용할겁니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/654d8a99-0de8-4dd5-a07c-a33815e95899/image.JPG" alt="">
lst.popleft()를 통해 값의 합이 15가 되기 전까지 원소를 계속 긁어옵시다.
<img src="https://velog.velcdn.com/images/ghost_dog/post/b6b171f6-ab94-4c3e-84eb-8262b04e1e0d/image.JPG" alt="">
그런데 긁어오다 보니 값의 합이 15를 넘은 24가 되었습니다!
이러면 조건에 만족하는 부분수열이니 해당 부분수열의 크기를 따로 기록하고 
최종적인 목적은 15가 넘는 제일 작은 크기의 부분수열이니 quene를 pop해서 최대한 크기를 줄여봅시다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/4576ee5b-9775-4eae-b63f-56429c131960/image.JPG" alt="">
5를 popleft해도 19가 되므로 이 수열 크기(4)또한 따로 기록해두고 더 빼봅시다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/115d9422-e65e-419e-a25e-dd9775c89fee/image.JPG" alt="">
1을 popleft해도, 3을 popleft해도 15보다 크거나 같으므로 부분수열의 크기를 따로 기록해두고 더 뺴봅시다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/d5317ea4-dadf-4e8b-98bf-9d1f5b9b05f7/image.JPG" alt="">
드디어 10만 남게되어 조건을 만족하지 못합니다. 이제 윗부분으로 다시 돌아가서 합산 15이상이 될때까지 lst에서 원소를 다시 긁어옵시다. </p>
<p>투포인터란 이처럼 시작점과 끝점을 잡고 조건을 만족하지 못하면 끝점을 늘리고 조건을 만족하면 시작점을 빼는 형식의, 이중 for문으로 O(N^2)에 처리되는 작업을 2개의 포인터의 움직임으로 O(N)에 해결하는 알고리즘입니다.
<img src="https://velog.velcdn.com/images/ghost_dog/post/1bfa2bb9-0912-4c64-9be1-5aef96ec234c/image.png" alt=""></p>
<p>출력값을 통해 과정을 살펴보면 위와 같은 느낌입니다.</p>
<hr>
<h1 id="코딩">코딩</h1>
<p>위 과정을 코딩해봅시다.</p>
<pre><code>from collections import deque
import sys

input = sys.stdin.readline</code></pre><p>해당 문제는 시간제한이 0.5초로 매우 급박하므로 sysinput을 사용해줍니다. </p>
<pre><code>N,S = map(int,input().split())
lst = deque(list(map(int,input().split())))</code></pre><p>배열의 크기, 합산 조건, lst등을 받아주고</p>
<pre><code>quene = deque([lst[0]])</code></pre><p>투포인터를 사용할 que를 하나 만들어줍니다. </p>
<pre><code>    cum_sum = lst[0]
    lst.popleft()
    min = 100000001</code></pre><p>한가지 주의할 점이 있습니다. 부분배열의 합산값을 구할때 sum(부분배열)을 하게되면 sum 함수를 100000번 가까이 사용해야 하므로 시간제한에 거릴 확률이 굉ㅡ장히 높습니다. 따라서 누적합을 cum_sum을 통해 따로 기재해주겠습니다. </p>
<pre><code>    while True:
        try:
            if cum_sum &lt; S:
                tmp1 = lst.popleft()
                cum_sum += tmp1
                quene.append(tmp1)</code></pre><p>만약 부분수열의 합이 위 예시에서 봤던 것처럼 15보다 작아 조건을 만족하지 못한다면 lst에서 원소를 긁어와 quene에 추가해줍시다. </p>
<pre><code>            else:
                if len(quene) &lt; min:
                    min = len(quene) 
                tmp2 = quene.popleft()
                cum_sum -= tmp2</code></pre><p>만약 조건을 만족한다면 조건을 만족하는 quene의 길이 중에서 제일 작인 길이를 quene에 넣어주도록 합니다. </p>
<pre><code>        except:
            break</code></pre><p>quene을 다써서 예외가 날 경우엔 break으로 처리해주고 </p>
<pre><code>print(min)</code></pre><p>마지막으로 min을 출력하면 끝날 것 같지만
<img src="https://velog.velcdn.com/images/ghost_dog/post/d0f05d7a-33a3-40a9-9288-ed574d91e2b3/image.png" alt="">
골드의 벽은 그렇게 호락호락하지 않습니다...</p>
<hr>
<h1 id="반례">반례</h1>
<p>로직이 올바른데 틀리게 나온다면 항상 반례를 생각해봐야 합니다. </p>
<blockquote>
<ol>
<li>테스트케이스가 10/10/11113211111 일때</li>
</ol>
</blockquote>
<p>한 가지 숫자가 조건 S를 만족시킬만큼 S보다 크게 나와버린다면 답은 무조건 1이 됩니다. </p>
<blockquote>
<ol start="2">
<li>min이 제 역할을 못해서 그대로 나와버릴 경우 </li>
</ol>
</blockquote>
<p>만약에 뭘 해도 합산 S가 만족되지 않는다면</p>
<pre><code>            else:
                if len(quene) &lt; min:
                    min = len(quene) 
                tmp2 = quene.popleft()
                cum_sum -= tmp2</code></pre><p>구문 자체가 실행되지 않아 최솟값을 구하려고 둔 min값이 초기값 100000001로 그대로 유지되게 됩니다. 
이 두 경우를 고쳐봅시다.</p>
<hr>
<pre><code>for values in lst:
    if values &gt;= S:
        boolean = True
if boolean == True:
    print(1)
else:</code></pre><p>투포인터 코드를 실행시키기 전에 원소들을 다 검사해서 S보다 크거나 같은 원소가 있으면 1을 출력해줍시다. </p>
<pre><code>    if min &gt;= 100000001:
        print(0)
    else:
        print(min)</code></pre><p>min값이 초기화값 그대로 나와버린다면 S조건을 아예 만족시키지 못하는 경우이므로 0을 출력합시다. </p>
<hr>
<h1 id="전체코드">전체코드</h1>
<pre><code>from collections import deque
import sys

input = sys.stdin.readline
N,S = map(int,input().split())
lst = deque(list(map(int,input().split())))
quene = deque([lst[0]])
sum_list = sum(lst)
boolean = False
for values in lst:
    if values &gt;= S:
        boolean = True
if boolean == True:
    print(1)
else:
    cum_sum = lst[0]
    lst.popleft()
    min = 100000001

    while True:
        try:
            if cum_sum &lt; S:
                tmp1 = lst.popleft()
                cum_sum += tmp1
                quene.append(tmp1)
            else:
                if len(quene) &lt; min:
                    min = len(quene) 
                tmp2 = quene.popleft()
                cum_sum -= tmp2
        except:
            break

    if min &gt;= 100000001:
        print(0)
    else:
        print(min)</code></pre><hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/b6a59030-40dc-4bf6-98bc-f31eeea406e4/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/dfaa20ca-06f6-49ee-98e0-494a838ad4aa/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.21 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.21-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.21-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Wed, 21 Feb 2024 05:48:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>프로그래머스 - 가장 먼 노드</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/e4f81288-448a-44df-ae43-6e31eb3b255b/image.png" alt=""></p>
</blockquote>
<p>DFS로 풀다가 한참 해맸습니다... 심지어 예전에 유사문제 한번 풀어봤던 것 같은데...
어쨋든 이 문제의 쟁점은 BFS로 level(depth)를 구할 수 있는가 없는가 입니다.</p>
<p>그림과 같이 노드의 간선이 2개 이상인 경우에는 DFS로 노드의 level을 구하기 매우매우매우 어렵기때문에 BFS로 레벨을 구하는 형식으로 접근해야 합니다. </p>
<pre><code>from collections import deque
import sys</code></pre><p>시간초과 방지용으로 sysinput 받아서 </p>
<pre><code>def solution(n, edge):
    input = sys.stdin.readline
</code></pre><p>solution 함수 내에 넣어줍시다. </p>
<pre><code>    def bfs(n):
        level = 1
        que.append(n)
        visited[n] = 1</code></pre><p>먼저 BFS 함수를 작성할껍니다. 초기 레벨 0은 따로 지정해줄꺼고 레벨 1부터 시작합시다. 
지금 막 들어온 노드 1은 방문처리 해주고  que에 넣어줍시다. </p>
<pre><code>        while que:
            for i in range(len(que)):
                a = que.popleft()</code></pre><p>BFS level처리의 핵심입니다. BFS를 딱 que값만큼만 돌리면 해당 level값만큼만 노드를 탐색한 샘이 되기 때문에 BFS에서 level을 구할 수 있습니다. 자세한 건 제 예전 포스팅 첨부하겠습니다. 
<a href="https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.19-%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4">https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.19-%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</a></p>
<pre><code>                for i in range(len(graph[a])):
                    if not visited[graph[a][i]]:
                        visited[graph[a][i]] = 1
                        que.append(graph[a][i])</code></pre><p>현재 탐색중인 노드를 인덱스 차원으로 접근하여 graph[노드] = 연결된노드 형식으로 짤껍니다. 
만약 탐색중인 노드와 연결된 노드 중 아직 탐색을 안한 노드가 있다면 방문처리를 해주고 que에 추가하여 탐색 예비후보로 지정합시다.</p>
<pre><code>                        ans[graph[a][i]] = level
                        print(graph[a][i],level)
            level += 1</code></pre><p>ans라는 리스트에 해당 노드에 접근하면 level을 표시하도록 코딩해줍시다. 
그리고 que에 해당하는 걸 다 돌았으면 (현재 level이 끝났으면) level을 1 올려주도록 합시다. </p>
<pre><code>    graph = [[] for _ in range(n+1)]
    while edge:
        [x,y] = edge.pop() 
        graph[x].append(y)
        graph[y].append(x)</code></pre><p>시간초과를 피하는 부분입니다.
원래 저는 그래프를 짤 때 습관적으로 </p>
<blockquote>
</blockquote>
<pre><code>    graph = [[0]*(n+1) for _ in range(n+1)]
    while edge:
        [x,y] = edge.pop() 
        graph[x][y] = graph[y][x] = 1</code></pre><p>로 작성했었는데 이러면 정상적으로 작동은 하나 DFS는 물론이고 BFS에서까지도 [0]이 쓸데없는 공간을 잡아먹어 시간초과가 뜨곤 했습니다. 따라서 [0]으로 선언하기보단 아예 빈 리스트에 인덱스만 집어넣을 수 있도록 만들어주고 해당 인덱스에 접근하면 value 값으로 그와 연결된 노드만 표시하게끔 코딩했씁니다. </p>
<pre><code>    visited = [0]*(n+1)
    que = deque([])
    ans = [0]*(n+1)
    ans[1] = 0
    bfs(1)</code></pre><p>방문배열 초기화, que선언, ans 선언 등을 해주고 맨 첫 노드인 1은 level 0으로 설정해줍시다. 그리고 1을 시작으로 bfs를 돌리면 됩니다. </p>
<pre><code>    return ans.count(max(ans))</code></pre><p>마지막으로 level이 가장 큰 노드의 갯수를 구하라 했으므로 max(ans)로 가장 큰 레벨을 구해 ans에 얼만큼 있는지 count해줍시다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/ccf62892-1eb4-4162-a7d4-910f20b321e2/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>프로그래머스 - 스킬트리</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/dc6afd0f-109f-4f42-bd89-10b806fdd7d6/image.png" alt=""></p>
</blockquote>
<p>소마 1,2번에 구현 문제가 나온다길래 구현 문제 연습겸 풀어봤습니다. </p>
<p>스킬과 스킬트리라는 변수가 주어집니다. 이 스킬이라는 리스트에 A,B,C가 있으면 스킬트리라는 원소값 순서는 무조건 A가 먼저, 그다음 B, 그다음 C가 와야합니다. 뭐가 중간에 껴있어도 크게 상관없습니다. </p>
<p>이 문제의 핵심은 스킬 리스트에 빠진 원소가 존재하는 경우입니다. 예를 들어 A-&gt;K(아무거나)-&gt;C라면 A다음에 와야 할 B가 빠져있으므로 스킬트리가 될 수 없습니다. </p>
<pre><code>    ans = 0
    skill = list(skill)</code></pre><p>일단 ans를 0으로 선언하고 skill을 list로써 활용하기 쉽게 분리해주겠습니다.</p>
<pre><code>    for i in range(len(skill_trees)):
        tmp = -1
        boolean = False</code></pre><p>이제 스킬 트리를 탐색하여 스킬 리스트와 대조해줄 겁니다. tmp값은 기재하겠지만 해당 스킬 트리 어느 인덱스에 스킬이 위치하는가를 기재해줄 겁니다. 값을 작으면~ 이라는 조건으로 대조할거라 -1로 초기화하였습니다. </p>
<pre><code>        for k in range(len(skill)):
            if boolean == True:
                break</code></pre><p>스킬 리스트와 대조를 시작하겠습니다. boolean값이 true면 스킬 리스트 순서에 맞지 않다고 가정하여 루프를 깨겠습니다. </p>
<pre><code>            elif skill[k] not in skill_trees[i]:
                tmp = 100
                continue</code></pre><p>스킬 리스트가 A-&gt;B-&gt;C 라고 가정하고 스킬트리가 A-&gt;K-&gt;C라고 가정해봅시다. A조사는 무난히 넘어갔는데 스킬트리에 B라는 스킬 리스트가 없는 걸 확인할 수 있습니다. 이렇게 되버리면 B이후의 어떤 스킬 리스트도 스킬트리에 존재해서는 안되므로 tmp에 100이라는 고값을 대입해 무슨 값이든 튕겨버리도록 합시다. </p>
<pre><code>            elif skill_trees[i].index(skill[k]) &lt; tmp:
                boolean = True
                continue</code></pre><p>만약 현재 조사중인 스킬 트리 내부의 스킬 리스트 위치가 이전에 조사한 스킬 트리 내부의 스킬 리스트 위치보다 작다면 스킬 리스트 순서에 맞지 않으므로 boolean = True를 유도해서 최종 답을 올리지 못하도록 합시다.</p>
<pre><code>            tmp = skill_trees[i].index(skill[k])</code></pre><p>다음 스킬 리스트와 비교하기 위해서 tmp를 저장해줍시다. </p>
<pre><code>        if boolean == False:
            ans += 1
    return (ans)</code></pre><p>위 과정을 걸쳤음에도 불구하고 boolean값이 False가 나왔다면 최종 답 + 1을 해주고 return 해줍시다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/eda23720-c1d0-4de0-95ab-f2fa8c7eeb42/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.20 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.20-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.20-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Tue, 20 Feb 2024 04:30:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>프로그래머스 - 바탕화면 정리하기</strong></p>
</blockquote>
<div class="main-section tab-content">
  <div class="guide-section" style="width: calc(40% - 12px);">
    <div class="tab-pane fade active show" id="tour2" aria-describedby="popover923405">
      <div class="guide-section-description">
        <h6 class="guide-section-title">문제 설명</h6>
        <div class="markdown solarized-dark"><p>코딩테스트를 준비하는 머쓱이는 프로그래머스에서 문제를 풀고 나중에 다시 코드를 보면서 공부하려고 작성한 코드를 컴퓨터 바탕화면에 아무 위치에나 저장해 둡니다. 저장한 코드가 많아지면서 머쓱이는 본인의 컴퓨터 바탕화면이 너무 지저분하다고 생각했습니다. 프로그래머스에서 작성했던 코드는 그 문제에 가서 다시 볼 수 있기 때문에 저장해 둔 파일들을 전부 삭제하기로 했습니다.</p>
><p>컴퓨터 바탕화면은 각 칸이 정사각형인 격자판입니다. 이때 컴퓨터 바탕화면의 상태를 나타낸 문자열 배열 <code>wallpaper</code>가 주어집니다. 파일들은 바탕화면의 격자칸에 위치하고 바탕화면의 격자점들은 바탕화면의 가장 왼쪽 위를 (0, 0)으로 시작해 (세로 좌표, 가로 좌표)로 표현합니다. 빈칸은 ".", 파일이 있는 칸은 "#"의 값을 가집니다. 드래그를 하면 파일들을 선택할 수 있고, 선택된 파일들을 삭제할 수 있습니다. 머쓱이는 최소한의 이동거리를 갖는 한 번의 드래그로 모든 파일을 선택해서 한 번에 지우려고 하며 드래그로 파일들을 선택하는 방법은 다음과 같습니다.</p>
><ul>
<li><p>드래그는 바탕화면의 격자점 S(<code>lux</code>, <code>luy</code>)를 마우스 왼쪽 버튼으로 클릭한 상태로 격자점 E(<code>rdx</code>, <code>rdy</code>)로 이동한 뒤 마우스 왼쪽 버튼을 떼는 행동입니다. 이때, "<strong>점 S에서 점 E로 드래그한다</strong>"고 표현하고 점 S와 점 E를 각각 드래그의 시작점, 끝점이라고 표현합니다.</p></li>
<li><p>점 S(<code>lux</code>, <code>luy</code>)에서 점 E(<code>rdx</code>, <code>rdy</code>)로 드래그를 할 때, "<strong>드래그 한 거리</strong>"는 |<code>rdx</code> - <code>lux</code>| + |<code>rdy</code> - <code>luy</code>|로 정의합니다.</p></li>
<li><p>점 S에서 점 E로 드래그를 하면 바탕화면에서 두 격자점을 각각 왼쪽 위, 오른쪽 아래로 하는 직사각형 내부에 있는 모든 파일이 선택됩니다.</p></li>
</ul>
><p>예를 들어 <code>wallpaper</code> = [".#...", "..#..", "...#."]인 바탕화면을 그림으로 나타내면 다음과 같습니다.<br>
<img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/ec8b3f44-17e9-4044-8117-fad0f1f4402f/eg1.png" title="" alt="eg1.png"><br>
이러한 바탕화면에서 다음 그림과 같이 S(0, 1)에서 E(3, 4)로 드래그하면  세 개의 파일이 모두 선택되므로  드래그 한 거리 (3 - 0) + (4 - 1) = 6을 최솟값으로 모든 파일을 선택 가능합니다.<br>
<img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/e69e8776-4e56-4abb-b2a7-3dc695620ef4/eg1-2.png" title="" alt="eg1-2.png"><br>
(0, 0)에서 (3, 5)로 드래그해도 모든 파일을 선택할 수 있지만 이때 드래그 한 거리는 (3 - 0) + (5 - 0) = 8이고 이전의 방법보다 거리가 늘어납니다.</p>
><p>머쓱이의 컴퓨터 바탕화면의 상태를 나타내는 문자열 배열 <code>wallpaper</code>가 매개변수로 주어질 때 바탕화면의 파일들을 한 번에 삭제하기 위해 최소한의 이동거리를 갖는 드래그의 시작점과 끝점을 담은 정수 배열을 return하는 <code>solution</code> 함수를 작성해 주세요. 드래그의 시작점이 (<code>lux</code>, <code>luy</code>), 끝점이 (<code>rdx</code>, <code>rdy</code>)라면 정수 배열 [<code>lux</code>, <code>luy</code>, <code>rdx</code>, <code>rdy</code>]를 return하면 됩니다.</p>
><hr><h4>제한사항</h4>
<ul><li> 1 ≤ <code>wallpaper</code>의 길이 ≤ 50</li><li>1 ≤ <code>wallpaper[i]</code>의 길이 ≤ 50
<ul>
<li><code>wallpaper</code>의 모든 원소의 길이는 동일합니다.</li>
</ul></li>
<li><code>wallpaper[i][j]</code>는 바탕화면에서 <code>i + 1</code>행 <code>j + 1</code>열에 해당하는 칸의 상태를 나타냅니다.</li>
<li><code>wallpaper[i][j]</code>는 "#" 또는 "."의 값만 가집니다.</li>
<li>바탕화면에는 적어도 하나의 파일이 있습니다.</li>
<li>드래그 시작점 (<code>lux</code>, <code>luy</code>)와 끝점 (<code>rdx</code>, <code>rdy</code>)는 <code>lux</code> &lt; <code>rdx</code>, <code>luy</code> &lt; <code>rdy</code>를 만족해야 합니다.</li>
</ul>
<hr>
><h4>입출력 예</h4>
<table class="table">
        <thead><tr>
<th>wallpaper</th>
<th>result</th>
</tr>
</thead>
        <tbody><tr>
<td>[".#...", "..#..", "...#."]</td>
<td>[0, 1, 3, 4]</td>
</tr>
<tr>
<td>["..........", ".....#....", "......##..", "...##.....", "....#....."]</td>
<td>[1, 3, 5, 8]</td>
</tr>
<tr>
<td>[".##...##.", "#..#.#..#", "#...#...#", ".#.....#.", "..#...#..", "...#.#...", "....#...."]</td>
<td>[0, 0, 7, 9]</td>
</tr>
<tr>
<td>["..", "#."]</td>
<td>[1, 0, 2, 1]</td>
</tr>
</tbody>
      </table>
<hr>
><h4>입출력 예 설명</h4>
<p>입출력 예 #1</p>
<ul>
<li>문제 설명의 예시와 같은 예제입니다. (0, 1)에서 (3, 4)로 드래그 하면 모든 파일을 선택할 수 있고 드래그 한 거리는 6이었고, 6보다 적은 거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 [0, 1, 3, 4]를 return합니다.</li>
</ul>
<p>입출력 예 #2</p>
<ul>
<li><p>예제 2번의 바탕화면은 다음과 같습니다.</p>
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/8bf4e2ba-1700-4231-a6ed-c18455919928/eg2.png" title="" alt="eg2.png"></p>
  <p>(1, 3)에서 (5, 8)로 드래그하면 모든 파일을 선택할 수 있고 이보다 적은 이동거리로 모든 파일을 선택하는 방법은 없습니다. 따라서 가장 적은 이동의 드래그로 모든 파일을 선택하는 방법인 [1, 3, 5, 8]을 return합니다.</p></li>
</ul>
<p>입출력 예 #3</p>
<ul><li><p>예제 3번의 바탕화면은 다음과 같습니다.</p>
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/7cc308f7-b8d7-482e-9e06-18bc1133aea0/eg3.png" title="" alt="eg3.png"></p>
<p>모든 파일을 선택하기 위해선 바탕화면의 가장 왼쪽 위 (0, 0)에서 가장 오른쪽 아래 (7, 9)로 드래그 해야만 합니다. 따라서 [0, 0, 7, 9]를 return합니다.</p></li>
</ul>
<p>입출력 예 #4</p>
<ul>
<li><p>예제 4번의 바탕화면은 다음과 같이 2행 1열에만 아이콘이 있습니다.</p>
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/5f726562-04dd-4056-8dd7-e58d1519f6ec/eg4.png" title="" alt="eg4.png"></p>
<p>이를 드래그로 선택하기 위해서는 그 칸의 왼쪽 위 (1, 0)에서 오른쪽 아래 (2, 1)로 드래그 하면 됩니다. (1, 0)에서 (2, 2)로 드래그 해도 아이콘을 선택할 수 있지만 이전보다 이동거리가 늘어납니다. 따라서 [1, 0, 2, 1]을 return합니다.</p></li>
</ul>
</div>
      </div>
    </div>

<hr>
<p>소마 테스트가 프로그래머스에서 진행된다길래 맛 좀 보려고 놀러왔습니다. 
어려울 것 없는 간단한 리스트 활용 문제입니다.</p>
<p>wallpaper에서 #이 가장 빨리 나오는 인덱스가 첫번째 x, 가장 나중에 나오는 인덱스가 두번째 x, wallpaper의 원소들 중에서 #이 가장 빠른 위치에 있는 인덱스가 첫번째 y, 마찬가지로 가장 나중에 나오는 인덱스가 두번째 y라고 생각하시면 됩니다. </p>
<pre><code>from collections import deque

def solution(wallpaper):
    x = []
    y = []
    sum = 0</code></pre><p>deque 하나 import 하고 시작하겠습니다. </p>
<pre><code>    wallpaper = deque(wallpaper)
    while wallpaper:
        tmp = wallpaper.popleft()
        if &#39;#&#39; in tmp:
            x.append(sum)</code></pre><p>popleft를 통해 개별 원소마다 접근하고
위에 언급한대로 wallpaper의 요소들중에 #이 있다면 sum값을 활용해서 해당 인덱스를 x 리스트에 싹 다 기록합니다. </p>
<pre><code>
        for values in tmp:
            if values == &#39;#&#39;:
                y.append(sum2)
            sum2 += 1
        sum += 1</code></pre><p>개별 원소 안에 #이 존재한다면 해당 위치를 y리스트에 싹 다 기록합니다.</p>
<pre><code>    return [min(x),min(y),max(x)+1,max(y)+1]</code></pre><p>마지막으로 값 return 해주면 됩니다. 
    <img src="https://velog.velcdn.com/images/ghost_dog/post/47302608-30f2-415f-8498-530286112a6b/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>프로그래머스 - 단어 변환</strong>
두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.</p></p>
</blockquote>
<div class="highlight"><pre class="codehilite"><code>1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다.
2. words에 있는 단어로만 변환할 수 있습니다.
</code></pre></div>
<p>예를 들어 begin이 "hit", target가 "cog", words가 ["hot","dot","dog","lot","log","cog"]라면 "hit" -&gt; "hot" -&gt; "dot" -&gt; "dog" -&gt; "cog"와 같이 4단계를 거쳐 변환할 수 있습니다.</p>
<p>두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.</p>
<h5>제한사항</h5>
<ul>
<li>각 단어는 알파벳 소문자로만 이루어져 있습니다.</li>
<li>각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.</li>
<li>words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.</li>
<li>begin과 target은 같지 않습니다.</li>
<li>변환할 수 없는 경우에는 0를 return 합니다.</li>
</ul>
<h5>입출력 예</h5>
<table class="table">
        <thead><tr>
<th>begin</th>
<th>target</th>
<th>words</th>
<th>return</th>
</tr>
</thead>
        <tbody><tr>
<td>"hit"</td>
<td>"cog"</td>
<td>["hot", "dot", "dog", "lot", "log", "cog"]</td>
<td>4</td>
</tr>
<tr>
<td>"hit"</td>
<td>"cog"</td>
<td>["hot", "dot", "dog", "lot", "log"]</td>
<td>0</td>
</tr>
</tbody>
      </table>
<h5>입출력 예 설명</h5>
<p>예제 #1<br>
문제에 나온 예와 같습니다.</p>
<p>예제 #2<br>
target인 "cog"는 words 안에 없기 때문에 변환할 수 없습니다.</p>
</div>
      </div>
    </div>




<hr>
<pre><code>def solution(begin, target, words):
global ans
words = list(sorted(words))</code></pre><p>   일단 정렬한 값과 정렬하지 않은 값이 다를 수 있기 때문에 단어 리스트 정렬부터 해줍시다. </p>
<pre><code>   begin: hit target: cog words: [hot, dot, dog, lot, log, cog] return : 4 &lt;&lt; 예제 1번
begin: hit target: cog words: [cog, log, lot, dog, dot, hot] return : 4</code></pre><p>   와 같은 경우에요.</p>
<pre><code>       def dfs(begin):
        global ans
        if begin in words:
            visited[words.index(begin)] = 1 </code></pre><p>DFS 작성을 시작합니다. 
현재 탐색중인 단어를 방문처리 해줍시다. </p>
<pre><code>        sum = 0
        for k in range(len(begin)):
            if begin[k] != target[k]:
                sum += 1
        if sum == 1:
            return ans</code></pre><p>현재 탐색중인 단어와 타겟 단어가 문자 1개 차이로 틀리다면 타겟 단어로 갈 수 있다는 것을 의미하므로 재귀를 끝내줍시다. </p>
<pre><code>        for i in range(len(words)):
            sum2 = 0
            for j in range(len(words[i])):
                if words[i][j] != begin[j]:
                    sum2 += 1
            if sum2 == 1 and not visited[i]:
                print(words[i])
                ans += 1
                dfs(words[i])
                return ans</code></pre><p>words 리스트에서 하나씩 꺼내서 만약 현재 탐색중인 단어하고 문자 1개 차이로 틀린 단어가 있다면 해당 단어를 탐색에 넣어 재귀를 돌려줍시다. 상단 코드에서 타켓 단어로 가는 것이 증명되었다면(dfs(words[i]) 아래 부분) ans를 return 하여 함수를 끝내주도록 합시다. </p>
<pre><code>    ans = 1
    visited = [0] * len(words)
    if target not in words:
        return 0
    else:
        return dfs(begin)</code></pre><p>target이 리스트 안에 없으면 타겟단어를 아예 만들 수 없는 것이므로 0을 return 하고 아니라면 dfs(begin)에서 나온 ans값을 return 하면 타겟단어까지 가는데 걸리는 단어 수 출력이 가능합니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/15615ff6-da54-4b4b-927b-ce44fc42b1ed/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>프로그래머스 - 등굣길</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/efb9cad2-fa35-4844-9dff-eb4a357e9f3f/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/fa88f246-3adc-4fc8-af41-08f4ddfe793f/image.png" alt=""></p>
</blockquote>
<p>10억이 넘는 수로 나누라는 것을 보자마자 알았습니다. 
네, DP 문제입니다. 
물웅덩이로는 통행이 불가하며 집에서 학교까지 가는 최적의 값을 도출하는 문제입니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p>저는 DP문제는 분석을 안하면 항상 푸는 데 난이도가 있더라고요.
해당 문제는 쟁점이 하나 있습니다. 바로 물웅덩이가 1열/1행에 생기는 경우입니다.</p>
<p><a href="https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.15-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4">https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.15-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</a></p>
<p>왜 1행/1열에 이해가 안가시면 이 게시물 한번 읽어보시면 도움되실 겁니다. </p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/3b450a29-65ae-4148-b6f2-bf8f4b4ce99e/image.jpg" alt=""></p>
<p>최단거리를 구하기 위해 1행과 1열을 전부 1로 초기화 시키고 있습니다. 
근데 어라? 물웅덩이가 1행과 1열에 생겼습니다. 그럼 이 이후는 어떻게 처리해야 할까요? 
<img src="https://velog.velcdn.com/images/ghost_dog/post/944b7ad4-1f54-45d1-b0e7-5a5163aab0e8/image.jpg" alt="">
정답은 장애물이 생긴 이후의 1행 또는 1열은 다 0으로 초기화해야 한다 입니다. 
왜냐하면 장애물이 생긴 이후의 공간은 어차피 오른쪽 또는  하강만 가능하므로 방문을 해봐야 최단 거리를 구하는 데 있어 전혀 메리트가 없기 때문입니다. (학교가 맨 아래 오른쪽에 있으니까)</p>
<hr>
<h1 id="코딩">코딩</h1>
<p>해당 내용을 생각하며 코딩을 해봅시다. </p>
<pre><code>def solution(m, n, puddles):
    graph = [[0]*m for _ in range(n)]

    for a in range(len(graph[0])):
        if [a+1,1] not in puddles:
            graph[0][a] = 1
        else:
            break
    for b in range(len(graph)):
        if [1,b+1] not in puddles: 
            graph[b][0] = 1
        else:
            break</code></pre><p>행,열,물웅덩이 위치를 받고 행열 크기만큼의 원소가 전부 0인 그래프를 생성해줍시다. 
만약 웅덩이가 없는 1행/1열이라면 해당 인덱스는 1로 초기화해주고 
행열 인덱스 값 1로 초기화중에 1행/1열 자리에 웅덩이가 발생했다! 하면 break로 나와서 원래 값인 0으로 냅둡시다. </p>
<pre><code>    for i in range(len(graph)):
        for j in range(len(graph[0])):
            if i==0 or j==0:
                continue</code></pre><p>이제 DP를 돌릴 준비를 합시다. 
각 인덱스마다 탐색을 하면서 만약 1행 또는 1열에 접근했다면 점화식을 위한 초기값이므로 건들지 말고 continue를 해줍시다. (어차피 1행 또는 1열은 가는 경우의 수가 1가지밖에 없으므로)</p>
<pre><code>            elif [j+1,i+1] in puddles:
                continue</code></pre><p>웅덩이라면 건들지 말고 continue 해줍니다. 이러면 점화식 적용이 안되어 왼쪽+위쪽 이전위치 최단거리 점화식을 사용할 때 이전위치의 웅덩이는 최단거리값이 아예 없는 효과를 줍니다. </p>
<pre><code>            else:
                graph[i][j] = graph[i-1][j] + graph[i][j-1]</code></pre><p>마지막으로 최단거리 구하는 점화식 자기자신 최단거리 = 위인덱스 최단거리 + 왼쪽인덱스 최단거리 
를 작성해주면 됩니다. </p>
<pre><code>    return graph[n-1][m-1]%1000000007</code></pre><p>출력은 DP 초과를 방지하기 위해 10억인가? 를 나눈 나머지를 출력하라고 문제에 명시되어있습니다. 
문제 취지는 좋은데 웅덩이 행 열이 뒤바껴서 들어가는 등 좀 미흡한 부분이 존재하는 문제였던 것 같습니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/1a44413c-d7a2-4348-bae2-ffe7a4a15d70/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.19 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.19-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.19-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Mon, 19 Feb 2024 05:26:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>*<em>18353번 - 병사 배치하기 *</em>
<img src="https://velog.velcdn.com/images/ghost_dog/post/2d130c5f-2bb3-4af1-9fb7-3efc02c4367e/image.png" alt=""></p>
</blockquote>
<p>가장 긴 오름차순 수열 문제에서 한번 꼰 문제입니다. 
분석을 할 수 있으면 어렵지 않게 풀 수 있습니다.</p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/6a48c47f-ee81-4f3a-9ccd-f54ca8d63abc/image.JPG" alt="">
위 수열에서는 2,5,3,2,15 라는 체크해둔 수열을 제외하면 병사의 수가 최대가 되도록 할 수 있습니다. 
그런데 체크해 둔 수열을 제외하면 뭔가 규칙성이 보이실 겁니다. 맞습니다. 바로 최장 증가 수열입니다.</p>
<p>한마디로 <strong>lst를 reverse 시킨 다음 전체 수열의 길이 - 최장 증가 수열의 길이</strong> 를  구한 값이 결국 열외해야 하는 병사의 수가 된다고 보시면 될 것 같습니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>N = int(input())
lst = list(map(int,input().split()))
lst = list(reversed(lst))
dp = [1]*len(lst)</code></pre><p>lst를 선언해주고 뒤집겠습니다. 갯수를 세야하므로 dp값은 전부 1로 초기화하겠습니다. </p>
<pre><code>for i in range(len(lst)):
    maximum = 0
    for j in range(i):</code></pre><p>최장 부분 수열을 구해주기 위해서 maximum을 하나 선언해줍니다. </p>
<pre><code>        if lst[i] &gt; lst[j]:
            if maximum &lt; dp[j] + 1:
                maximum = dp[j] + 1</code></pre><p>현재 조사중인 인덱스가 이전에 조사중인 인덱스보다 크다면
이전 dp값들 중에서 가장 큰 값 ( 인덱스 전까지의 가장 긴 최장 증가 수열 길이 ) + 1을 maximum에 대입해줍시다.
<img src="https://velog.velcdn.com/images/ghost_dog/post/f1416691-6d7f-4127-b58d-732b2355ef85/image.png" alt=""></p>
<p>dp값을 가시화하면 이러한 형태로 나오게 됩니다. </p>
<pre><code>                dp[i] = maximum</code></pre><p>그리고 해당 maximum에서 이미 수열 길이 + 1 처리를 해주었으니 dp값에 바로 넣어줍시다.</p>
<pre><code>print(len(lst)-max(dp))  </code></pre><p>마지막으로 전에 언급한대로 전체 수열의 길이 - 최장 증가 수열의 길이 를 출력하면 답이 나옵니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/5ca2c258-f61a-4e6c-bfc3-37c21e634042/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/21053890-2a9d-494a-b8ee-489f322c2065/image.png" alt=""></p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/9131bed6-a8b1-4f3e-b613-d33348195268/image.png" alt="">
소마 서류지원에 합격했습니다.</p>
<p>프로젝트 경험이 적고 무언가를 만들어보고 싶은 제 입장에서 소마 연수생이 된다면 더없이 좋은 기회일 것 같습니다.</p>
<p>제 실력이 아직 미흡하고, 아마 합격되지 않을 것이라는걸 잘 알지만 여태 공부한 게 헛되지 않도록 열심히 풀어보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.16 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.16-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.16-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 16 Feb 2024 04:41:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>11048번 - 이동하기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/80000b0a-4de9-4173-a0d5-7596fce066ed/image.png" alt=""></p>
</blockquote>
<p>미로가 주어지고 각 미로의 칸에 사탕이 놓여있다고 가정할 때, 리스트 (0,0) 에서 (N,M)까지 가는데 주울 수 있는 사탕의 최댓값을 구하는 문제입니다. </p>
<p>언뜻 보면 BFS류라고 생각할 수 있으나 DP로 해결 가능합니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/cf3bd3f2-f08a-46ec-bc9b-4711c4a0c11d/image.JPG" alt=""></p>
<p>어제 저는 14494번을 통해 이차원 리스트에서 DP로 최단거리를 구하는 방법에 대해 익혔습니다. 
이 문제도 그러한 방식으로 잘 생각해보면 풀이가 나옵니다. </p>
<p>위 그림에 적어놨듯이 </p>
<p>리스트[i][[j]의 최대 사탕 갯수는 i,j의 옆까지 도달하는데 주워온 사탕의 최대 갯수(dp[i][j-1])와 i,j의 위까지 도달하는데 주워온 사탕의 최대 갯수(dp[i-1][j])를 대조해 더 큰값 + 리스트[i][j]의 사탕갯수</p>
<p>를 계산하게 되면 해당 점화식이 도출되게 됩니다.  </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>N,M = map(int,input().split())
lst = []
for _ in range(N):
    lst.append(list(map(int,input().split())))</code></pre><p>먼저 문제에서 주어진 미로를 받아줍시다. </p>
<pre><code>dp = [[0]*M for _ in range(N)]
dp[0][0] = lst[0][0]</code></pre><p>dp리스트를 미로 크기와 똑같이 생성하고 dp[0][0]을 점화식을 위해 lst[0][0]과 똑같이 설정해줍니다. </p>
<pre><code>for i in range(N):
    for j in range(M):
        dp[i][j] = max(dp[i][j-1],dp[i-1][j]) + lst[i][j]</code></pre><p>이제 분석단계에서 도출한 점화식을 적용하면 됩니다. </p>
<pre><code>if N == 1:
    print(sum(lst[0]))
else:
    print(dp[N-1][M-1])</code></pre><p>마지막 출력부분에서 한가지 주의할 점은 N이 1인 경우입니다. 
N이 1이면 세로길이가 1이 되어 점화식을 참고하여 도출하면 이상한 값이 도출됩니다. </p>
<p>세로길이가 1이라면 어차피 미로의 전체 합이 사탕의 최대 갯수가 되므로 미로의 합산을 출력하고
아니라면 도착지점의 dp값을 도출하면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/87289d36-f6fd-41d7-a899-4123daaf0082/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/5e81ad3e-4b1f-437b-97e6-1abd80b1d8a2/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.15 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.15-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.15-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Thu, 15 Feb 2024 05:49:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>14494번 - 다이나믹이 뭐에요?</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/ea057082-2aa5-4625-9a95-438ca0d850dd/image.png" alt=""></p>
</blockquote>
<p>이차원 배열이 주어질 때 시작점에서 끝점까지 가는데의 최단 경로의 수를 구하는 문제입니다. 
문제에서 구체적으로 명시하지는 않았는데, 최단 경로가 맞습니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/9a305e5e-5b93-4bc5-bb47-43e9cd998e90/image.png" alt="">
먼저 최단 경로는 이러한 식으로 생겼습니다. 
문제에서 친절히 맨 위에서 아래,오른쪽으로만 가는 최단 경로의 점화식은</p>
<h4 id="dpij--dpi-1j--dpij-1">dp[i][j] = dp[i-1][j] + dp[i][j-1]</h4>
<p>이라고 언급이 되어있습니다. 
하지만 문제는 아래와 오른쪽 뿐만이 아닌 대각선으로도 이동이 가능하다고 되어있습니다. 
그래서 점화식에 대각선을 추가하게 되면</p>
<h4 id="dpij--dpi-1j--dpij-1--dpi-1j-1">dp[i][j] = dp[i-1][j] + dp[i][j-1] + dp[i-1][j-1]</h4>
<p>라는 점화식 도출이 가능해집니다. </p>
<p>이제 코딩에 들어갈껀데, 주의할 점은 세로축[0]과 가로축[0]은 위에 그림에 보시다시피 전부 1로 초기화를 진행해야 (해당 행/렬은 접근 가능한 경우의 수가 단 하나) 해당 점화식이 의미가 있습니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>n,m = map(int,input().split())
dp = [[0]*m for _ in range(n)]
dp[0][0] = 1
for a in range(n):
    dp[a][0] = 1
for b in range(m):
    dp[0][b] = 1</code></pre><p>이차원배열을 하나 선언하고 가로축의 0번째, 세로축의 0번째 행/열을 전부 1로 초기화해줍시다. </p>
<pre><code>for i in range(len(dp)):
    for j in range(len(dp[i])):
        if i == 0 or j == 0:
            continue
        else:
            dp[i][j] = dp[i-1][j] + dp[i][j-1] + dp[i-1][j-1] </code></pre><p>초기화한 라인을 제외한 구간은 전부 도출한 점화식을 적용시켜줍시다. </p>
<pre><code>print(dp[n-1][m-1]%1000000007)</code></pre><p>1000000007로 나눈 나머지를 문제에서 요구한대로 출력해줍시다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/60e074cd-2f18-4ea8-998d-fbedcc414a0c/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/2b40e4ed-78ae-416c-8540-a3d87ea6e134/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.06 코딩 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.06-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.06-%EC%BD%94%EB%94%A9-%EB%AC%B8%EC%A0%9C%ED%92%80</guid>
            <pubDate>Wed, 14 Feb 2024 06:57:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>2156번 - 포도주 시식</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/335241bd-0c88-47f8-8962-9de6d1aa3049/image.png" alt=""></p>
</blockquote>
<p>연속으로 3개를 고르면 안되고 마실수 있는 최대 포도주를 구하는 문제입니다.
전에 풀었던 계단 문제와 비슷한 감이 없잖아 있는데 조금 더 복잡합니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/d411e5f0-2e7d-4ffe-8184-c742205703af/image.JPG" alt=""></p>
<p>본 문제는 세 가지 경우로 나눠서 풀 수 있습니다. </p>
<p><strong>1. i번째 잔을 아예 고르지 않거나
2. i-3번째 잔 -&gt; i-1번째 잔 -&gt; i번째 잔을 통해 올라오거나
3. i-2번째 잔 -&gt; i번째 잔을 통해 올라오는 경우</strong></p>
<p>이렇게 세 가지로 정리할 수 있겠습니다. 
자세한 풀이가 필요한 분들은</p>
<p><a href="https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80">https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</a></p>
<p>를 참조하시면 원활하게 이해 가능하실 겁니다. </p>
<p>계단 오르기와 다른 점은 계단은 무조건 밟아야 하지만 이 포도주는 안마셔도 상관이 없으므로 안마시는 경우에는 이전까지의 최대 dp값 (dp[i-1])을 해당 dp에 넣어주면 되겠습니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>n = int(input())
lst = []
for _ in range(n):
    a = int(input())
    lst.append(a)</code></pre><p>리스트를 받아줍니다. </p>
<pre><code>dp = [0]*n
if n&gt;=1:
    dp[0] = lst[0]
if n&gt;=2:
    dp[1] = lst[0]+lst[1]
if n&gt;=3:
    dp[2] = max(lst[0]+lst[2],lst[1]+lst[2],dp[1])</code></pre><p>점화식에서 dp[i-3]이 최소 참조 인덱스이므로 3까지 기본값을 설정해줍니다. 다만, 주의할 점은 lst의 값이 2 이하일 경우 list out of range exception이 발생할 수 있으므로 1과 2값을 직접 지정해주도록 합시다. </p>
<pre><code>for i in range(3,n):
    dp[i] = max(lst[i]+dp[i-2],lst[i]+lst[i-1]+dp[i-3])
    if dp[i] &lt; dp[i-1]:
        dp[i] = dp[i-1]</code></pre><p>3부터는 아까 세운 점화식 중 최댓값을 dp[i]에 대입시켜주면 됩니다. </p>
<pre><code>print(max(dp))</code></pre><p>마지막으로 dp의 최댓값을 출력하면 </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/01c25bf2-05f6-4e99-ab08-1bbb2dcda4c5/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/bdd37c55-e89a-4d1a-9980-c08ea0a282c5/image.png" alt="">
정상적으로 해를 도출할 수 있습니다. </p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.06 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.06-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.06-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Tue, 06 Feb 2024 05:37:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>17216번 - 가장 큰 감소 부분 수열</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/b2b6a2cf-e820-4e83-b747-9ad52145a746/image.png" alt=""></p>
</blockquote>
<p>가장 큰 감소하는 부분 수열의 합을 구하는 문제입니다. </p>
<pre><code>import copy 
N = int(input())
lst = list(map(int,input().split()))
dp = copy.deepcopy(lst)
</code></pre><p>합의 최소는 자기 자신이므로 dp값은 기존 리스트 인덱스와 똑같이 초기화해줍니다. </p>
<pre><code>for i in range(len(lst)):
    for j in range(i):
        if lst[i] &lt; lst[j]:
            dp[i] = max(dp[i],dp[j]+lst[i])</code></pre><p>각 인덱스마다 순회를 돌며 자기 자신보다 큰 인덱스가 존재하면 자기 자신의 dp값과 자기자신+큰 인덱스의 dp값 을 대조하여 더 큰 값을 넣어주도록 합시다. </p>
<pre><code>print(max(dp))   
</code></pre><hr>
<p>해당 dp값에서 제일 큰 값을 출력하면 됩니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/4e1e77ec-3b2a-4fdd-8386-8f253ac6d19a/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/14b20b5c-f026-4372-9c76-ee1f083a9548/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.05 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.05-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.05-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Mon, 05 Feb 2024 03:23:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>11055번 - 가장 큰 증가하는 부분수열</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/944706e2-7af3-40e2-9fcd-6a63c6b928fc/image.png" alt=""></p>
</blockquote>
<p>가장 큰 증가하는 부분수열을 구하는 문제입니다. 
이전에 풀었던 가장 긴 증가하는 부분수열을 응용한 문제라고 보시면 될 것 같습니다. </p>
<pre><code>import copy

n = int(input())
lst = list(map(int,input().split()))
dp = copy.deepcopy(lst)</code></pre><p>먼저 dp를 list와 똑같이 초기화해줍니다. 왜냐하면 현재 문제에서 제일 작게 나올 수 있는 값은 부분 수열의 합산이므로 결과적으로 자기 자신입니다.</p>
<pre><code>for i in range(len(lst)):
    for j in range(i):
        if lst[i] &gt; lst[j]:
            dp[i] = max(dp[i],lst[i]+dp[j])</code></pre><p>그리고 현재 조사중인 인덱스가 이전 인덱스보다 더 크다면 현재 dp값과 그 이전 인덱스의 dp값 + 현재 인덱스의 합산을 대조하여 더 큰값을 현재 인덱스의 dp값에 대입해줍니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/cdd2d965-e62c-47b8-b232-94d85468c307/image.png" alt=""></p>
<p>해당 문제 테케대로라면 이런씩으로 나옵니다. </p>
<pre><code>print(max(dp))</code></pre><p>마지막으로 dp 출력해주면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/07044ee0-ef5c-4da0-84c6-1810703cd806/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/9f5c147d-b1af-4ff3-9362-8e148a7cbcab/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>1965번 - 상자넣기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/feae39e3-6a65-490b-8db4-7c577ada7bfa/image.png" alt=""></p>
</blockquote>
<p>가장 긴 증가하는 부분수열을 문제화한 문제입니다. </p>
<pre><code>n = int(input())
lst = list(map(int,input().split()))
dp = [1]*len(lst)</code></pre><p>1이 수열의 최솟값이므로 받아주고(여기선 박스)</p>
<pre><code>for i in range(len(lst)):
    for j in range(i):
        if lst[i] &gt; lst[j]:
            dp[i] = max(dp[i],dp[j]+1)</code></pre><p>현재값이 이전 값보다 크다면 현재값의 dp와 이전값의 dp + 1값을 비교해 더 큰 값을 현재값의 dp로 넣어 가장 긴 증가하는 부분수열을 구체화해줍시다. </p>
<pre><code>print(max(dp))</code></pre><p>max(dp)출력하면 됩니다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/6b6f2bde-348d-49aa-a518-bafffe51068e/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/1588c235-b2eb-4569-a9dd-31e56915a71c/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>15988번 - 1,2,3 더하기 3</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/20c5fbf0-2d71-42cf-bdbe-5b36d3a4ce9e/image.png" alt=""></p>
</blockquote>
<p>시간초과를 피하기 정말정말 어려운 문제입니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p>일단 추가 시간 없음을 보자마자 단순 재귀나 백트래킹으로는 절대 시간 안에 못맞춘다고 봐야합니다.
그리고 조건중에 <code>각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.</code> 라는 말이 있으면 DP라고 보는게 타당하겠죠...
<img src="https://velog.velcdn.com/images/ghost_dog/post/bedc073d-348b-4d0d-85ab-66251c760b93/image.JPG" alt="">
위와 같은 과정으로 dp[n] = dp[n-1]+dp[n-2]+dp[n-3]이라는 점화식 도출이 가능합니다. </p>
<hr>
<h1 id="첫-번째-시도">첫 번째 시도</h1>
<pre><code>T = int(input())
ans = []
for _ in range(T):
    ans.append(int(input()))</code></pre><p>테스트케이스를 받아 ans에 추가해주겠습니다. </p>
<pre><code>memo = [0]*(max(ans)+1)
memo[0] = 1
memo[1] = 2
memo[2] = 4</code></pre><p>memo[2]까지는 점화식에서 음수취급 될 수도 있으므로 2까지 초기화를 진행해줍시다. 시간을 아끼기 위해 테스트케이스 중 제일  큰 값만 돌리고 나머지는 메모 참고하여 답을 출력하려 했습니다. </p>
<pre><code>recursion(max(ans))</code></pre><p>이제 재귀를 돌려줍니다. </p>
<pre><code>import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline</code></pre><p>시간 절약을 위해 sysinput과 재귀 depth 상한을 해제해주고</p>
<pre><code>def recursion(n):
    if memo[n]:
        return memo[n]
    memo[n] = ( recursion(n-1) + recursion(n-2) + recursion(n-3) )%1000000009
    return memo[n]
</code></pre><p>메모이제이션에 있으면 memo[n]을 return, 아니라면 점화식에서 저 식을 나눈 값을 return 해줍시다. 
1000000009를 지금 나눠주는 이유는 메모리 제한으로 인해 솔브가 안되는 상황을 방지하기 위해서입니다. 
(분배법칙 / 모듈러 성질에 의거해 저기서 나누든 답 직전에 나누든 값 출력에 차이는 없습니다.)</p>
<pre><code>for values in ans:
    print(memo[values-1])</code></pre><p>출력시켜주면
<img src="https://velog.velcdn.com/images/ghost_dog/post/d144ba18-b58d-4bda-b6ef-40597062aa51/image.png" alt="">
답은 제대로 나오는데
<img src="https://velog.velcdn.com/images/ghost_dog/post/95eedeaf-513a-4596-a2da-ca8ce52504c0/image.png" alt="">
역시나...</p>
<hr>
<h1 id="두-번째-시도">두 번째 시도</h1>
<p>재귀는 가독성이 좋지만 callstack을 불러오므로 for문에 비해 상대적으로 시간복잡도가 높습니다. 
따라서 for문으로 다시 구현해보기로 했습니다. </p>
<pre><code>import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline</code></pre><p>sysinput및 setrecursionlimit 선언해주고</p>
<pre><code>T = int(input())
ans = []
for _ in range(T):
    ans.append(int(input()))</code></pre><p>테스트케이스를 받아 ans에 추가합니다. </p>
<pre><code>memo = [0]*(max(ans)+1)
memo[0] = 1
memo[1] = 2
memo[2] = 4</code></pre><p>DP 초기화를 진행해주고</p>
<pre><code>for i in range(3,len(memo)):
    memo[i] = ( memo[i-1] + memo[i-2] + memo[i-3] )% 1000000009</code></pre><p>초기화 다음 단계인 3부터 끝까지 DP를 돌려줍시다. </p>
<pre><code>for values in ans:
    print(memo[values-1])</code></pre><p>그리고 ans에서 가장 큰 값을 DP돌려 MEMO에 기록했으니 memo값을 참조하여 답을 출력하면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/8a7b6b3d-8333-4e94-b031-4a50d1bf7c56/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/4ff65722-6171-4433-9d52-7318f5f4a385/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.02 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.02-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.02-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 02 Feb 2024 06:51:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>1463번 - 1로 만들기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/ea36c318-66fa-423f-8aaf-b8f99bf125d2/image.png" alt=""></p>
</blockquote>
<p>세 가지 연산을 사용해서 주어진 수를 최대한 적게 연산하여 1로 만들때, 연산의 횟수를 구하는 문제입니다.</p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/f8ab9090-60bf-4183-b455-62edab14fbe9/image.JPG" alt=""></p>
<p> 이 문제는 점화식이 주어져 있다고 봐야합니다. 
 세 가지 연산, 3나누기, 2나누기, 1빼기를 점화식으로써 세울 수 있으면 풀이가 가능합니다. </p>
<p>** 1. 3나누기**
dp(9) = dp(3) + 1 이라는 결과가 나오며, dp[i] = dp[i//3] + 1을 도출할 수 있습니다. 
<strong>2. 2나누기</strong>
dp(4) = dp(2) + 1 이라는 결과가 나오며, dp[i] = dp[i//2] + 1을 도출할 수 있습니다.
<strong>3. 1빼기</strong>
dp[2] = dp[1] + 1 이라는 결과가 나오며, dp[i] = dp[i-1] + 1을 도출할 수 있습니다. </p>
<p>여기서 dp(N)의 결과값은 제일 적은 연산을 사용하여 N을 1로 만드는데 드는 연산수이므로, 저 세 경우 중 제일 적은 연산을 사용한 것을 하나 꼽아 현재 조사중인 수에 대입하면 되겠습니다.</p>
<p>점화식  : min(dp[i] = dp[i//3] + 1,dp[i] = dp[i//2] + 1,dp[i] = dp[i-1] + 1)</p>
<hr>
<h1 id="코딩">코딩</h1>
<p>위의 점화식처럼 min으로 비교하여 넣는 것은 불가능하므로 하나씩 비교하며 코딩하면 됩니다. </p>
<pre><code>dp = [0]*1000010

dp[1] = 0
N = int(input())</code></pre><p>dp배열을 하나 선언해주고 N을 입력받습니다. </p>
<pre><code>for i in range(2,N+1):
    dp[i] = dp[i-1] + 1</code></pre><p>일단 1뺀 경우를 dp[i]에 넣어줍시다. 뭘 먼저하든 상관없습니다. </p>
<pre><code>    if i%3==0:
        dp[i] = min(dp[i//3]+1,dp[i])</code></pre><p>그리고 해당 3나눈 경우랑 1뺀 경우랑 비교해서 더 작은걸 dp[i]에 넣어줍니다. </p>
<pre><code>    if i%2==0:
        dp[i] = min(dp[i//2]+1,dp[i])</code></pre><p>마지막으로 2나눈 경우랑 방금 전에 dp[i]에 넣은 값이랑 비교해서 더 작은 걸 dp[i]에 넣어줍니다. </p>
<pre><code>print(dp[N])</code></pre><p>마지막으로 dp[N]을 출력해주시면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/8e27bbba-8361-41d2-a880-9455db822f95/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/437c9401-c584-4a8a-af90-c72fbeb128e6/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.02.01 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.01-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.02.01-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Thu, 01 Feb 2024 05:39:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>10844 - 쉬운 계단 수</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/2db03c30-cfd9-4694-a216-9cb2dd86357a/image.png" alt=""></p>
</blockquote>
<p>쉽지않은 문제였습니다. </p>
<p>일단 위와같이 각 자릿수의 차이가 1인 수를 게단수라고 할 때, 자릿수가 주어지면 해당 자릿수에서 가능한 계단 수의 경우의 수의 합을 출력하면 되는 문제입니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/dfa7fab7-6153-4717-88cb-a6c6a683279c/image.JPG" alt="">이 문제가 어려운 이유로는 두 가지가 있습니다. </p>
<p>일단 첫번째로 규칙이 까다로워서 점화식 세우는 거 자체가 쉽지 않다는 거고, 두 번째로 점화식을 두 경우로 나누어서 생각해봐야 한다는 점입니다.</p>
<p>일단 첫번째  경우는 위와 같이 N으로 시작하는 계단 수의 경우의 수를 나열해봤을 때 처음에 보고 어? 이거 </p>
<p>dp[i][[j] = dp[i-1][j] + dp[i-1][j+1]인가? 또는
dp[i][[j] = dp[i-1][j-1] + dp[i-1][j]인가 싶었습니다. </p>
<p>한마디로 현재 조사중인 경우는 이전 자릿수의 인덱스 -1이거나 +1이라고 생각했습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/383dfe11-0c4f-4100-9063-271b4384fb3a/image.png" alt=""></p>
<p>그러나 두 경우 다 오답이 나옵니다. </p>
<p>해당 문제는 그림의 하늘/초록 형광펜처럼 점화식을 dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1] 이라는 위 상대각선 형태로 찾아야 풀이가 가능합니다. 덧붙여서 상대각선 형태여야지만 0번 인덱스를 조사할 때 (파이썬의 경우입니다) -1과 1을 참조하여 점화식으로 0번째 인덱스를 정상적으로 참조하는게 가능합니다. </p>
<p>두 번째 경우는 9로 시작하는 계단 수의 경우입니다. 
계단 수가 9로 시작하게 되면 다른 점화식이 적용되지 않습니다.</p>
<p>처음에는  dp[i][[j-1]을 참조하면 해결될까 싶었는데 역시 아니었습니다. 이 경우에는 그림의 노란 형광펜처럼 상 좌대각선의 수를 가져다 써야 정상적인 점화식이 가능합니다.</p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>N = int(input())
dp = [[0]*9 for _ in range(N)]
for first in range(9):
    dp[0][first] = 1</code></pre><p>dp배열을 만들어주고 첫번째 리스트는 다 1로 초기화해주겠습니다. </p>
<pre><code>for i in range(1,N):
    for j in range(9):</code></pre><p>2번째 리스트부터 1<del>N까지의 자릿수마다 0</del>8로 시작하는 계단 수의 경우를 대입하겠습니다. </p>
<pre><code>        if j==8:
            dp[i][j] = dp[i-1][j-1]</code></pre><p>위에 분석한대로 현재 9로 시작하는 계단 수의 경우의 수를 대입해주려면 상 좌대각선의 숫자를 기입해줍시다. </p>
<pre><code>        else:
            dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]</code></pre><p>그 경우가 아니라면 상 좌우대각선의 수를 하나씩 뽑아 더한 값을 현재 계단 수의 경우의 수에 넣어주면 됩니다. </p>
<pre><code>print(sum(dp[N-1])%1000000000)</code></pre><p>마지막으로 문제 조건대로 해당 자릿수의 계단 수의 합산에 10억을 나눈 나머지를 출력하면 됩니다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/a87efc84-355e-41d1-8992-a5ba76541a23/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/337267f8-afdd-451a-89d0-c04e10ea2713/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>1932번 - 정수 삼각형</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/6580f4b6-45ab-4d2e-8e02-ba8565254268/image.png" alt=""></p>
</blockquote>
<p>아래로 내려오면서 대각선으로 값을 합산할 수 있는데 최종적으로 내려왔을 때 가장 큰 값이 되는 수를 구하면 되는 문제입니다. </p>
<pre><code>import copy
N = int(input())
lst = []
for _ in range(N):
    lst.append(list(map(int,input().split())))
dp = copy.deepcopy(lst)</code></pre><p>일단 저 정수삼각형을 리스트로써 받고 DP 기록을 하기 위해 리스트를 deepcopy 해주겠습니다. </p>
<pre><code>for i in range(len(dp)-1):
    for j in range(len(dp[i])):</code></pre><p>그리고 위에서부터 끝-1 까지 합산을 위해 리스트를 순회하겠습니다. </p>
<pre><code>        if lst[i+1][j] + dp[i][j] &gt; dp[i+1][j]:
            dp[i+1][j] = lst[i+1][j] + dp[i][j]</code></pre><p>첫 번째 경우부터 살펴보겠습니다. 
현재 DP에는 나올 수 있는 최대값이 기록되어있다고 가정해야합니다. </p>
<p>바로 아래 있는 값 + 현재까지 기록된 최대값 이 바로 아래 기록될 최댓값보다 크다면 
바로 아래 기록될 최댓값 = 바로 아래 있는 값 + 현재까지 기록된 최대값으로 표기해줍시다. </p>
<pre><code>        if lst[i+1][j+1] + dp[i][j] &gt; dp[i+1][j+1]:
            dp[i+1][j+1] = lst[i+1][j+1] + dp[i][j]</code></pre><p>피라미드는 양 대각선 방향이므로 오른쪽에 있는 인덱스 하나 더 코딩해주시면 됩니다.</p>
<pre><code>print(max(dp[-1]))</code></pre><p>마지막으로 가장 아랫 값에서 가장 큰 값이 나올 수 있는 최대 경우의 수이므로 출력하시면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/98166a0f-6e58-4f4d-ad46-62bdd15a41f1/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/80458ba3-5252-43f2-af82-6bc58a02f555/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.31 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.31-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.31-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Wed, 31 Jan 2024 04:20:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>9184번 - 신나는 함수 실행</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/3afbbdf3-edd6-4fa2-8c1c-38f5f679ba6b/image.png" alt=""></p>
</blockquote>
<p>문제에 주어진 코드를 실행시키려고 하는데 
문제에서 해당 코드를 실행시키면 시간이 너무 오래걸릴까봐 걱정이랍니다. 
해당 코드를 시간초과가 나지 않게 실행시키는 코드를 작성하면 됩니다.</p>
<p>...보자마자 짐작이 갑니다. DP의 메모이제이션 기법을 사용하면 됩니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p>보통 팩토리얼같은데서 메모이제이션을 사용하면 변수를 하나만 받아서 memo[해당변수] 라는 씩으로 기록하는데 이번에는 변수가 무려 세 개입니다. </p>
<p>보고 딱 떠오른 생각은 3차원 리스트로써 memo[a][b][c] 로 구현하는게 정해겠구나 생각했습니다. </p>
<p>그러나 저는 남들과 같은 길을 걷기는 싫어 조금 창의적으로 구현해봤습니다. </p>
<p>일단 메모이제이션으로써 딕셔너리를 하나 만들고, 3개 변수를 묶은거 </p>
<p>(예를 들어 w(20,20,20) 이라면 dict[&#39;202020&#39;] = 해당 재귀문 (w(20,20,20)이라던가 w(a,b,c-1) + w(a,b-1,c-1) - w(a,b-1,c) 와 같은 등등...) )</p>
<p>와 같은 형식으로 딕셔너리를 구성해준 후 </p>
<pre><code>    if str(a)+str(b)+str(c) in memo.keys():
        return memo[str(a)+str(b)+str(c)]</code></pre><p>해당 값이 key값에 존재한다면 해당 값의 key에 해당하는 value를 내보내는 식으로 구현해도 될 것 같았습니다. </p>
<hr>
<h1 id="첫번째-시도">첫번째 시도</h1>
<pre><code>while True:
    a,b,c = map(int,input().split())
    if a==-1 and b==-1 and c==-1:
        break
    print(&#39;w(%s, %s, %s) =&#39;%(a,b,c),w(a,b,c))</code></pre><p>먼저 문제에서 -1,-1,-1이 들어오면 입력을 멈추고 그 전까지는 출력하라 했으므로 위와 같이 출력 형식에 맞추어 코딩해줍니다. </p>
<pre><code>memo = dict()

def w(a,b,c):
</code></pre><p>다음으로 메모이제이션으로써 딕셔너리를 하나 선언하고 w함수 제작을 시작합니다. </p>
<pre><code>    if str(a)+str(b)+str(c) in memo.keys():
        return memo[str(a)+str(b)+str(c)]</code></pre><p>아까 말한대로 현재 탐색중인 값이 메모이제이션의 키값에 있으면 해당 value 값으로 return 해줍시다.</p>
<pre><code>    if a&gt;20 or b&gt;20 or c&gt;20:
        memo[str(a)+str(b)+str(c)] = w(20,20,20)
        return memo[str(a)+str(b)+str(c)]</code></pre><p>나머지는 문제에 나온 코드 그대로 작성하되 메모이제이션 기법을 사용할 수 있게 메모이제이션에 넣어 해당 메모이제이션 값으로 return 해주도록 합시다.</p>
<pre><code>    if a&gt;20 or b&gt;20 or c&gt;20:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(20,20,20)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]

    if a&lt;b and b&lt;c:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(a,b,c-1) + w(a,b-1,c-1) - w(a,b-1,c)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]
    else:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(a-1,b,c) + w(a-1,b-1,c) + w(a-1,b,c-1) - w(a-1,b-1,c-1)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]</code></pre><p>나머지도 동일한 방식으로 작성하면 됩니다. 
이제 출력해보니까 
<img src="https://velog.velcdn.com/images/ghost_dog/post/ba8c368f-beea-4268-a4f1-a11b46612824/image.png" alt="">
값은 잘나오는데 
<img src="https://velog.velcdn.com/images/ghost_dog/post/b04da682-4566-4470-a4da-a1fd663f1d76/image.png" alt="">
알수 없는 이유로 틀립니다. </p>
<hr>
<h1 id="오답-원인-분석">오답 원인 분석</h1>
<p>곰곰히 생각해보니까 메모이제이션 작성을 할때 w(11,11,11)과 같은 형식을 기재받게 되면 memo[&#39;111111&#39;]이 되는데 </p>
<p>이게 나중에 메모 참고를 할때 w(1,111,11)이냐, w(1111,1,1)이냐 구분할 방법이 없기 때문에 나는 오류라고 생각이 됩니다. </p>
<p>그래서 w(a,b,c)사이를 구분해줄 문자를 아무거나 하나 넣어주기로 했습니다. </p>
<hr>
<h1 id="해결">해결</h1>
<pre><code>
memo = dict()

def w(a,b,c):

    if a&lt;=0 or b&lt;=0 or c&lt;=0:
        return 1

    if str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c) in memo.keys():
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]

    if a&gt;20 or b&gt;20 or c&gt;20:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(20,20,20)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]

    if a&lt;b and b&lt;c:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(a,b,c-1) + w(a,b-1,c-1) - w(a,b-1,c)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]
    else:
        memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)] = w(a-1,b,c) + w(a-1,b-1,c) + w(a-1,b,c-1) - w(a-1,b-1,c-1)
        return memo[str(a)+&#39;empty&#39;+str(b)+&#39;empty&#39;+str(c)]

while True:
    a,b,c = map(int,input().split())
    if a==-1 and b==-1 and c==-1:
        break
    print(&#39;w(%s, %s, %s) =&#39;%(a,b,c),w(a,b,c))</code></pre><p>단순히 a와b사이, 그리고 b와 c사이 메모이제이션을 원활히 참고될 수 있도록 &#39;empty&#39;라는 문자열을 넣어 구분해주기로 했습니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/310de1ba-daab-4183-80bb-f823a3f012ec/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/44f48de7-28f2-43b0-8557-9f28915e1169/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>11057번 - 오르막 수</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/13228893-8601-417e-aec7-c8e1922003e5/image.png" alt=""></p>
</blockquote>
<p>위와 같이 자리수가 주어졌을 때 해당 자리수에서 나올 수 있는 오르막 수가 나오는 경우를 모두 구하는 문제입니다. </p>
<hr>
<h1 id="분석-1">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/2ba75988-fec9-4b52-a8ef-340bdd34733d/image.JPG" alt="">
일단 저 위에 0,1,2,3,4,5,... 는 마지막 자릿수입니다. 
그러니까 수의 자리수가 1이라면 0으로 끝나는 오르막 수는 1개 (0), 수의 자리수가 2라면 1로 끝나는 오르막 수는 2개(수가 0으로 시작할 수 있댔으니가 00,01) 이런 씩입니다. </p>
<p>이렇게 쭉 나열해보면 규칙성이 보입니다. 
위에 표기해둔 3자리수의 2로 끝나는 수(6) = 3자리수의 1로 끝나는 수(3) + 2자리수의 2로 끝나는 수(3)
라는 규칙성을 찾아볼 수 있습니다. </p>
<p>이걸 토대로 점화식을 세워보면</p>
<blockquote>
<p><strong>N자리수의 i로 끝나는 수 = N자리수의 i-1로 끝나는 수  + N-1자리수의 i로 끝나는 수</strong></p>
</blockquote>
<p>이런 형식일 겁니다.</p>
<p>DP느낌대로 1자리수는 죄다 초기 고정으로 박아두고, 2자리수부터는 위 점화식을 기반으로 풀이하면 될 것 같습니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>dp = [[0]*10 for _ in range(1001)]
N = int(input())</code></pre><p>자리수는 1001까지 나올 수 있다니까 0~9(끝나는 수) X 1001 짜리 이차원 리스트를 DP배열로써 제작해주겠습니다. 그리고 N자리수를 받습니다.</p>
<pre><code>for i in range(10):
    dp[1][i] = 1</code></pre><p>DP를 쓰기위한 초기 계산식 1자리수를 전부 1로 초기화해줍시다. </p>
<pre><code>for i in range(2,N+1):
    for j in range(10):
        dp[i][j] = dp[i-1][j] + dp[i][j-1]</code></pre><p>2자리수부터는, 0~9까지의 끝나는 수에 아까 세운 점화식 N자리수의 i로 끝나는 수 = N자리수의 i-1로 끝나는 수  + N-1자리수의 i로 끝나는 수 =&gt; dp[i][j] = dp[i-1][j] + dp[i][j-1]
을 넣어줍시다. 여기서 i는 자릿수, j는 끝나는 수라고 생각하시면 될 것 같습니다. </p>
<pre><code>print(sum(dp[N])%10007)</code></pre><p>마지막으로 N에 해당하는 배열 (0~9까지의 끝나는 수 기반 오르막 수의 개수들)의 합산을 10007로 나눈 값(문제 조건)을 출력하면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/6d98dd44-21d9-4735-8ff4-746f06bc1775/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/10695101-ca79-4cf1-89a9-49864ec8374a/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.30 소마대비 문제풀이]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.30-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.30-%EC%86%8C%EB%A7%88%EB%8C%80%EB%B9%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Tue, 30 Jan 2024 04:06:00 GMT</pubDate>
            <description><![CDATA[<p>안될 가능성이 높으나 15기 소프트웨어 마에스트로에 지원했습니다. 뭐든 도전해보는게 중요하니까요.</p>
<p>따라서 하던 DP 잠시 미뤄두고 소마 기출 푸는거에 집중하려 합니다. 기출에 DP도 포함되어 있으니 겸사겸사 공부하겠습니다. </p>
<blockquote>
<p><strong>1012 - 유기농 배추</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/97be3dc0-b5b4-4347-b855-ad78479e0985/image.png" alt=""></p>
</blockquote>
<p>BFS로 1이 뭉쳐있는 구역 갯수를 파악하는 문제입니다. </p>
<pre><code>T = int(input())
que = deque([])
ans = []</code></pre><p>테스트케잇,큐,답을 출력할 ans를 선언하겠습니다. </p>
<pre><code>for _ in range(T):

    M,N,K = map(int,input().split())
    graph = [[0]*M for _ in range(N)]
    visited = [[0]*M for _ in range(N)]</code></pre><p>테스트케이스만큼 루프를 돌려주고, 가로,세로,배추가 심어져 잇는 위치를 받아서</p>
<pre><code>
    for _ in range(K):
        a,b = map(int,input().split()) 
        graph[b][a] = 1</code></pre><p>그래프에 표기해줍니다. 주의할 점은 이곳 그래프의 표기 방식은 열과 행이 반대라서 a와 b를 거꾸로 입력받아야 정상적으로 출력됩니다. </p>
<pre><code>
    cnt = 0
    for i in range(N):
        for j in range(M):
            if not visited[i][j] and graph[i][j] == 1:
                bfs(i,j)
                cnt += 1</code></pre><p>다음으로 cnt변수를 선언하고 그래프를 순회하는 동안 방문하지 않은 지역이고 해당 지역이 1이라면 BFS 탐색으로 돌아줍니다. 탐색 후에는 cnt변수를 1 올려줍니다. </p>
<pre><code>    ans.append(cnt)

for values in ans:
    print(values)</code></pre><p>cnt변수를 ans에 추가하고 values로써 출력하면 답이 출력됩니다. </p>
<pre><code>from collections import deque
dx = [-1,1,0,0]
dy = [0,0,1,-1]
</code></pre><p>BFS는 항상 먹던 그 맛입니다. dx,dy로 상하좌우 탐색 설정하고</p>
<pre><code>def bfs(x,y):
    que.append([x,y])
    visited[x][y] = 1</code></pre><p>탐색에 들어왔으면 해당 위치는 방문여부 O로 표기 해줍니다.</p>
<pre><code>    while que:
        x,y = que.popleft()
        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y
            if nx&lt;0 or ny&lt;0 or nx&gt;=N or ny&gt;=M:
                continue</code></pre><p>4방향 탐색을 실시하는동안 그래프 밖을 벗어나려하면 continue로 막아주고 </p>
<pre><code>            if not visited[nx][ny] and graph[nx][ny] == 1:
                visited[nx][ny] = 1
                que.append([nx,ny])</code></pre><p>만약에 탐색하다가 방문을 안했고 해당 지역이 1이라면 탐색 후보지에 넣어주고 방문여부 O로 표기해주면 됩니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/d7867170-624e-4cf4-bf24-7c32405ab8d6/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/55fd3d6a-4845-45e3-9a56-173d0c04eccd/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>9461번 - 파도반 수열</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/1422fe6e-9f97-48e8-988e-7ce8acb3ead0/image.png" alt=""></p>
</blockquote>
<p>그림과 같은 삼각형이 나선 모양으로 놓여저있고 P(N)은 N번째 삼각형 변의 길이라 할때, P(N)을 구하시오.</p>
<p>라는 DP문제입니다. </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/57090962-77fb-4693-b70b-f309d52b6df4/image.JPG" alt=""></p>
<p>먼저 저는 P(4)에서 점화식을 도출하려고 봤더니 
P(4) = P(3)+P(1) 그리고 P(4) = P(3)+P(2) 라는 식이 두개가 도출되어 점화식을 세우는게 힘들었습니다. </p>
<p>그래서 확실히 구분되는 지점을 봤더니 P(11)을 살펴보면 P(11) = P(10) + P(6)인데 P(10)과 P(6)은 각각 9와 3인 수열에 하나밖에 없는 고유 수라 P(11)의 연산이 가능한 경우는 저것밖에 없었습니다. </p>
<p>따라서 본 문제에서 P(n)은 P(n-1)+P(n-5)라는 점화식 도출이 가능합니다.</p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>T = int(input())
for _ in range(T):
    n = int(input())
    print(dp(n-1))</code></pre><p>먼저 테스트케이스를 받고 받은만큼 dp함수를 재귀로써 돌려줍니다. 
n-1로 변수에 넣은 이유는 문제는 P(1)부터 시작하지만 저는 P(0)부터 시작하기 때문입니다. </p>
<pre><code>memo = [0]*101
def dp(N):
    if N == 0 : return 1
    if N == 1 : return 1
    if N == 2 : return 1
    if N == 3 : return 2
    if N == 4 : return 2</code></pre><p>메모이제이션을 하나 만들어주고 0,1,2 첫 세 삼각형은 1로 초기화, 3,4 두개의 삼각형은 3,4로 초기화를 진행해줍니다. 3,4를 굳이 넣어주는 이유는 예를 들어 초기화 식 없이 점화식 P(4)를 작동하게 되면 P(3) + P(-1)이라는 음수를 불러오는 사태가 발생하므로 이를 방지하기 위함입니다. </p>
<pre><code>    if memo[N]:
        return memo[N]
    memo[N] = dp(N-1) + dp(N-5)
    return memo[N]</code></pre><p>메모이제이션에 있으면 해당 값으로 return 하고 아니라면 현재 탐색중인 N값을 메모이제이션에 넣고 해당 값으로 return 하면 됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/6c915eff-4ce0-4cdf-831a-e5ba2526bbc9/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/a463a51b-a5ad-41b0-b94a-592684c2219e/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.29 오늘의 코딩공부]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.29-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.29-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Mon, 29 Jan 2024 07:21:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>11053번 - 가장 긴 증가하는 부분 수열 (LIS)</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/dbdcbe96-06d3-4225-afe0-07653d3ca0cf/image.png" alt=""></p>
</blockquote>
<p>가장 긴 증가하는 부분 수열을 탐색하는 문제입니다. 
이 문제는 이분 탐색과 DP 두 가지 방법으로 풀이할 수 있으나 저는 DP 연습중이므로 DP로 풀겠습니다. </p>
<hr>
<h1 id="알고리즘-분석">알고리즘 분석</h1>
<p>DP로 풀이하는 방법은 다음과 같습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/3d9c4030-4ec4-469a-9a47-bd66d5d57d89/image.JPG" alt="">
위와 같이 생긴 리스트와 DP가 존재한다면 
<img src="https://velog.velcdn.com/images/ghost_dog/post/db534555-284f-4fc0-a7df-e050b6b1d574/image.JPG" alt="">
이러한 씩으로 현재 조사값 이전 값들 중에서 작은 값들이 존재한다면 해당 작은 값들의 DP값들 중에서 제일 큰 DP + 1을 현재 조사값 DP에 넣어주는 씩으로 코딩하면 됩니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>N = int(input())
lst = list(map(int,input().split()))
dp = [1]*len(lst)</code></pre><p>먼저 수열의 최소 길이는 1이므로 1을 len(lst)만큼 만들어줍니다. </p>
<pre><code>for i in range(len(lst)):
    maximum = 0
    for j in range(i):</code></pre><p>현재 조사값은 lst[i], 이전 값들 조사는 lst[j]로 맡겠습니다. </p>
<pre><code>        if lst[j] &lt; lst[i]:
            if maximum &lt; dp[j]:
                maximum = dp[j]
            dp[i] = maximum + 1      </code></pre><p>만약 현재 조사값보다 이전 조사값이 더 작다면, 이전 조사값들 중 제일 큰 DP를 maximum에 넣어주고 maximum + 1을 현재 조사값의 DP에 기록하겠습니다. </p>
<pre><code>print(max(dp))</code></pre><p>그중에서 가장 큰 값을 출력하면 됩니다.
<img src="https://velog.velcdn.com/images/ghost_dog/post/2242d2e9-b4e1-47c6-9158-428cfa4c0fb4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/b73baf94-4b7e-4aa9-a6a5-57c50de499dd/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>11722번 - 가장 긴 감소하는 부분 수열</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/97c48a6f-a626-4d0e-b7ad-c83d98b6f3a2/image.png" alt=""></p>
</blockquote>
<p>원리만 알면 거의 보너스 문제라고 할 수 있습니다.
마찬가지로 현재 조사 값 이전 값들 중에서 큰 게 존재한다면 이전 값들 max(DP) + 1을 현재 조사값 DP에 대입시켜주면 되는 방식입니다.</p>
<pre><code>N = int(input())
lst = list(map(int,input().split()))
dp = [1]*len(lst)

for i in range(len(lst)):
    maximum = 0
    for j in range(i):
        if lst[i] &lt; lst[j]:
            if maximum &lt; dp[j]:
                maximum = dp[j]
            dp[i] = maximum + 1

print(max(dp)) 
</code></pre><p><img src="https://velog.velcdn.com/images/ghost_dog/post/f686c2b5-e799-4f46-aea8-34b6a31e1af6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/5a17392d-18d0-4f60-b4a2-2ad1edfcea0a/image.png" alt=""></p>
<hr>
<h1 id="오답-풀이중">오답 풀이중</h1>
<blockquote>
<p><strong>15686번 - 치킨 배달</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/18d12206-3739-427c-a8e2-36a548d13f90/image.png" alt=""></p>
</blockquote>
<p>거의 다 푼거 같은데 안풀리는 문제입니다. 
지난번 풀이 후 질문을 올려봤는데
<img src="https://velog.velcdn.com/images/ghost_dog/post/05d95778-cf70-4631-8f2f-01dc03f9ad9b/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/e2819013-f76a-4ed0-b230-f9b08c6ab339/image.png" alt="">
라고 친절히 답변해주셔서 저 문제를 살펴봤습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/7ded8d64-1f47-48ef-bbe9-b2c14cd5224e/image.png" alt="">
? ㅋㅋㅋㅋㅋㅋ
이게 어떤 말씀하시는지는 감이 잡히는데 (조합 코드 작성할 때 visited를 쓰지 말고 count 변수 증가 형식으로 재귀를 돌려서 백트래킹 부분 시간복잡도를 줄여라? 이런 느낌인 것 같습니다)</p>
<p>정말 한 5시간동안 고민해도 결국 못풀었습니다. 다른 문제 좀 더 살펴보다가 풀어야 할 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[15686번 못 푼 문제 - 2024.01.26]]></title>
            <link>https://velog.io/@ghost_dog/15686%EB%B2%88-%EB%AA%BB-%ED%91%BC-%EB%AC%B8%EC%A0%9C-2024.01.26</link>
            <guid>https://velog.io/@ghost_dog/15686%EB%B2%88-%EB%AA%BB-%ED%91%BC-%EB%AC%B8%EC%A0%9C-2024.01.26</guid>
            <pubDate>Fri, 26 Jan 2024 06:07:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>*<em>15686번 - 치킨 배달 *</em>
<img src="https://velog.velcdn.com/images/ghost_dog/post/0c81c214-f949-4e93-8f21-5945af9efb40/image.png" alt=""></p>
</blockquote>
<pre><code>from collections import deque
import sys
import copy

input = sys.stdin.readline

dx = [-1,1,0,0]
dy = [0,0,1,-1]

def bfs(x,y):
    visited[x][y] = 1
    que.append([x,y])
    while que:
        x,y = que.popleft()
        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y
            if nx&lt;0 or ny&lt;0 or nx&gt;=N or ny&gt;=N:
                continue 
            if not visited[nx][ny]:
                if graph[nx][ny] == 2:
                    return graph[x][y]                    
                if graph[nx][ny] &gt;= 0:
                    graph[nx][ny] = graph[x][y] + 1
                    visited[nx][ny] = 1
                    que.append([nx,ny])

N,M = map(int,input().split())
graph = []
visited = [[0]*N for _ in range(N)]
distance = 0
que = deque()
for _ in range(N):
    a = list(map(int,input().split()))
    graph.append(a)
graph_copy = copy.deepcopy(graph)


#살려둘 치킨집을 구하는 경우의 수 
def combination(chicken):
    if len(tmp) == M:
        chicken_choice.append(tmp[:])
        return 
    else:
        for i in range(len(chicken)):
            if visited_chicken[i]:
                continue
            tmp.append(chicken[i])
            visited_chicken[i] = 1
            combination(chicken)
            tmp.pop()
            visited_chicken[i] = 0

tmp = []
chicken = []
chicken_choice = []
for p in range(N):
    for q in range(N):
        if graph[p][q] == 2:
            chicken.append([p,q])
visited_chicken = [0]*len(chicken)

combination(chicken)

distance = 0
ans = []


#치킨거리 구하기 
while chicken_choice:
    choice = chicken_choice.pop()
    for m in range(N):
        for o in range(N):
            if graph[m][o] == 2:
                graph[m][o] = 0
    while choice:
        [x,y] = choice.pop()
        graph[x][y] = 2
    graph_copy2 = copy.deepcopy(graph)

    for i in range(N):
        for j in range(N):
            if graph[i][j] == 1:
                distance += bfs(i,j)
                visited = [[0]*N for _ in range(N)]
                graph = copy.deepcopy(graph_copy2)
                que.clear()                
    ans.append(distance)
    distance = 0
    graph = copy.deepcopy(graph_copy)

print(min(ans))</code></pre><p>BFS와 Combination으로 시도하였으나 시간초과 및 메모리 제한으로 실패.
이어서 BFS를 제외한 순수한 점대점 거리로 계산하였으나 역시 실패.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.25 오늘의 코딩공부]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80-sx3t6n9f</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80-sx3t6n9f</guid>
            <pubDate>Thu, 25 Jan 2024 03:51:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>11726번 - 2*N 타일링</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/8103aa2a-eb2d-413e-9bba-e5e7ea680df0/image.png" alt=""></p>
</blockquote>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/b138da7b-eaab-4a3d-982a-eb6b6b1bca3b/image.JPG" alt="">
이런 문제입니다. </p>
<p>2xn이라는 총 공간이 주어졌을 때 1x2타일과 2x1타일을 이용해서 이 공간을 채울 수 있는 경우의 수를 구하면 되는 문제입니다. </p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/9cadfef6-7439-47d3-b411-a98e425bffc0/image.JPG" alt="">
이런 경우 두 가지 유형으로 나눌 수 있는데
먼저 타일이 1x2타일로 끝나는 경우 와 타일이 2x1 타일로 끝나는 경우입니다. 
따라서 점화식은 DP(N) = DP(N-1) + DP(N-2)라는 형태로 작성할 수 있습니다. </p>
<hr>
<h1 id="코딩">코딩</h1>
<pre><code>N = int(input())
print(dp(N)%10007)</code></pre><p>입력을 받고 큰값이 나오니까 해를 10007로 나누라 했으므로 문제대로 따라줍니다. </p>
<pre><code>memo = [0] * 1001
def dp(n):
    if n == 1:
        return 1
    if n == 2:
        return 2</code></pre><p>최대 값이 1000이므로 메모이제이션 크기는 1001로 설정해주겠습니다. 
1인 경우 1가지 경우밖에 안되므로 1 return, 2인경우 2가지 경우밖에 안되므로 2 return 하고</p>
<pre><code>    if memo[n]:
        return memo[n]</code></pre><p>메모이제이션에 데이터가 존재할 경우 해당 데이터를 return</p>
<pre><code>    memo[n] = dp(n-1) + dp(n-2)
    return memo[n]</code></pre><p>아까 도출한 점화식을 return 해주면 최종적으로 DP를 활용해 문제풀이가 가능합니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/201e2c15-d67f-465e-b149-eada7e7421bc/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/f5dd9d7e-f26d-4484-9494-4ad56119dc42/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>11727번 - 2*N 타일링 2</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/22189529-64f0-4a5c-b3fd-8d2c1c38ffbc/image.png" alt=""></p>
</blockquote>
<p>위 문제를 응용한 문제입니다. 
이번엔 2x2 타일이 하나 추가됬네요.
초기값하고 점화식만 조금 바꿔주면 될듯 싶습니다. </p>
<pre><code>N = int(input())
print(dp(N)%10007)</code></pre><p>N을 입력받고</p>
<pre><code>memo = [0] * 1001
def dp(n):
    if n == 1:
        return 1
    if n == 2:
        return 3</code></pre><p>길이가 2일때 2x2타일 하나, 1x2타일 2번, 2x1타일 2번 총 3번으로 만들수 있기 때문에 3으로 초기화를 진행해줍시다. </p>
<pre><code>    if memo[n]:
        return memo[n]
    memo[n] = dp(n-1) + dp(n-2) + dp(n-2)
    return memo[n]</code></pre><p>그리고 마지막 부분에 2x2타일도 올 수 있으므로 dp(n-2)를 하나 더 추가하면 정답을 도출할 수 있습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/5089323c-2d73-4c13-8bfe-d342ebbed84d/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/1439326a-f9ae-40f2-88dc-cc2b54f021e0/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>2133번 - 타일 채우기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/9a0f8980-7996-4663-b0a3-b1e549affe1f/image.png" alt=""></p>
</blockquote>
<p>타일 채우기 끝판왕 문제입니다.
이번에는 크기가 3xN인 벽을 1x2, 2x1 타일로 채우는 방법을 계산해야 합니다. </p>
<hr>
<h1 id="분석-1">분석</h1>
<p>*<em>1. 타일 값 선언 *</em></p>
<p>타일을 채우기 시작하는 부분(초기화값)부터 한번 살펴보겠습니다. 
일단 공간이 1인 경우에는 타일을 세워서 공간을 못만듭니다!
그리고 공간이 2인 경우에는 
<img src="https://velog.velcdn.com/images/ghost_dog/post/e79b4c1e-d1ac-47a8-abda-9f7d0a66972e/image.JPG" alt="">
이렇게 3개의 경우로 스타트를 끊을 수 있겠습니다. 
그리고 제가 하나 간과했던 점이 있는데 GPT한테 물어보니 명쾌한 해답을 내놓더군요. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/ce73148a-ba52-46fa-91aa-520da9ed5399/image.png" alt="">
그건 바로 공간이 0인 경우도 하나의 경우의 수로 취급되어 1로 스타트를 끊을 수 있다는 겁니다!
이제 이걸 코드로 표현하면</p>
<pre><code>    if n == 0:
        return 1
    if n == 1:
        return 0
    if n == 2:
        return 3</code></pre><p>이런 느낌으로 초기값을 선언하고 시작할 수 있습니다. </p>
<p>*<em>2. 타일을 끝맺을 때 경우의 수 *</em>
<img src="https://velog.velcdn.com/images/ghost_dog/post/95c47000-fab2-40a3-b142-2e2f55a6388a/image.JPG" alt=""></p>
<p>이게 좀 복잡합니다. 
일단 위의 예시처럼 2칸만 남았을 때는 3 X DP(N-2) 값으로 점화식을 세우는 게 가능합니다. </p>
<p>그런데 이제 제가 그려둔 오른쪽 예시를 보시면 2XDP(N-짝수) 만큼 계속해서 점화식을 세우는 게 가능하여 결과적으로 N-i가 0이 될때까지 점화식을 세우는 게 가능해집니다...</p>
<p>For문을 사용하여 N-i가 0이 될때까지 점화식을 세우고 메모이제이션에 추가해주는 방식으로 코딩해야 문제풀이가 가능하겠습니다. </p>
<hr>
<h1 id="코딩-1">코딩</h1>
<pre><code>N = int(input())
print(dp(N))</code></pre><p>N 입력을 받아주고 dp(N)을 실행시켜줍니다.</p>
<pre><code>memo = [0] * 1001
def dp(n):  
    tmp = 0

    if n == 0:
        return 1
    if n == 1:
        return 0
    if n == 2:
        return 3</code></pre><p>메모이제이션을 하나 만들어주고 위에 분석처럼 시작 크기에 알맞게 return 값을 대입해줍시다. </p>
<pre><code>    if memo[n]:
        return memo[n]</code></pre><p>메모이제이션에 있는 값이면 해당 메모로 값을 return 해주고 </p>
<pre><code>    tmp = 3 * dp(n-2)</code></pre><p>없는 값이면 일단 2칸 남았을 경우의 점화식을 세우고 해당 값을 tmp에 추가시키겠습니다. </p>
<pre><code>    for i in range(3,n+1):
        if i%2 == 0:
            memo[n] += 2 * dp(n-i)</code></pre><p>핵심 부분입니다. </p>
<p>4칸 이상의 짝수칸이 남았을 경우에는 2 X DP( N - 반복문변수)라는 점화식을 0이 되기 전까지 계속해서 돌려줍니다. </p>
<p>해당 부분은 4칸,6칸,8칸...계속해서 N 전까지 경우의 수가 늘어날 여지가 있기 때문에 for문안에 점화식을 넣어 처리해주도록 합시다. </p>
<pre><code>    return memo[n]</code></pre><p>마지막으로 return 메모이제이션을 하면 정답을 도출할 수 있습니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/60ba3ad0-9575-4d2a-90cc-c44d1125e97f/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/5c42e268-32c1-4367-becd-5dcb6e2a63ee/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.24 오늘의 코딩공부]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.24-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Wed, 24 Jan 2024 03:28:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>9095번 - 1,2,3 더하기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/19ae190d-299f-41ca-83fb-a31ad0e28956/image.png" alt=""></p>
</blockquote>
<p>DP문제인데 그냥 재귀로 풀어버렸습니다 하하...</p>
<pre><code>N = int(input())</code></pre><p>입력받을 횟수를 받고</p>
<pre><code>for _ in range(N):
    cnt = 0
    ans = []    
    a = int(input())
    dp(0,a)
    print(cnt)</code></pre><p>변수를 받아 재귀를 돌려줍니다. </p>
<pre><code>def dp(start,end):
    global cnt
    if start &gt;= end:
        cnt += 1
        return </code></pre><p>만약 start변수가 end와 같아지거나 더 커진다면 cnt를 하나 올리고 함수를 종료합니다. </p>
<pre><code>    for i in range(1,4):
        if sum(ans) + i &gt; end:
            continue</code></pre><p>1,2,3을 사용하여 해당 숫자를 어떻게 만드는지가 관건이므로 1~3까지 for문 재귀를 통해 구현하겠습니다. 돌리다가 만약 저장해둔 숫자(1,1,1...) + i(1or2or3) 이 end보다 커진다면 continue를 써서 해당 for문은 스킵하겠습니다. </p>
<pre><code>        ans.append(i)</code></pre><p>만약 더해서 커지지 않는다면 해당 경우의 수를 구하기 위해 i를 ans배열에 추가하고</p>
<pre><code>        dp(start+i,end)</code></pre><p>재귀로 다시 들어가서 예를 들어 합이 4라면 1111 구하고 </p>
<pre><code>        ans.pop()</code></pre><p>pop 해서 1112,1113,1114 다 continue에 막히고 111은 끝났으니 112실행 =&gt; 합산 4니까 cnt+1 이런씩으로 작동됩니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/33cc2504-f2af-4e4c-aa96-4de74485c281/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/f6408d02-81da-4dfa-a839-b304f93f5e23/image.png" alt=""></p>
<hr>
<blockquote>
<p><strong>2579 - 계단 오르기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/6b0974b8-e1e1-4acd-83ac-6567f20b6d8e/image.png" alt=""></p>
</blockquote>
<p>제대로 된 DP 문제입니다. 
DP는 점화식을 세우는게 제일 중요하니 만큼 문제 분석도 잘 해야 사용이 가능합니다.  </p>
<hr>
<h1 id="분석">분석</h1>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/2070b1bd-ab2e-419d-a7c4-e9c18ff87a45/image.JPG" alt="">
해당 문제는 최대값으로 계단을 오르면 점수가 몇점인가 묻는 문제입니다. 
최대값으로 오를 수 있는 경우가 두 가지가 있는데, </p>
<ol>
<li>바로 직전 계단에서 마지막 계단으로 올라올 때</li>
<li>전전 계단에서 마지막 계단으로 올라올 때</li>
</ol>
<p>이 두가지 경우입니다.</p>
<p>마지막 계단을 기준점으로 삼은 이유는 N번째 계단을 밟아야만 끝난다는 제약이 있기 때문입니다. </p>
<p> 1번과 같은 경우는 계단을 연속으로 최대 두 번만 밟을 수 있다는 제약이 있으므로, 위의 그림에 그려둔 것과 같이 N-3번째 계단을 밟고 N-1번째 계단을 밟아야 마지막 계단인 N번째 계단을 밟을 수 있습니다. </p>
<p>반면, 2번과 같은 경우는 제약이 없으므로 N-2번째 계단을 밟고 바로 마지막 계단인 N번째 계단을 밟는 게 가능합니다. </p>
<p>이를 식으로 세워두면</p>
<ol>
<li>DP(N-3)(N-3까지의 최대값) + stair(N-1)(N-1의 계단 점수) + stair(N)(N의 계단 점수)</li>
<li>DP(N-2)(N-2까지의 최대값) + stair(N)(N의 계단 점수) </li>
</ol>
<p>라는 형식으로 세울 수 있습니다. </p>
<hr>
<h1 id="코드">코드</h1>
<pre><code>N = int(input())
stair = []
for _ in range(N):
    a = int(input())
    stair.append(a)</code></pre><p>일단 계단의 개수하고 계단에 적힌 값들을 입력받아줍시다. </p>
<pre><code>if N == 1:
    print(stair[0])
elif N == 2:
    print(stair[0]+stair[1])</code></pre><p>1을 입력받았을때와 2를 입력받았을때는 점화식 스타트가 N-3부터이기 때문에 예외로 저렇게 처리해줍니다. </p>
<pre><code>else:
    #dp 초기화
    dp = [0]*301
    dp[0] = stair[0]
    dp[1] = stair[0]+stair[1]
    dp[2] = max(stair[0] + stair[2], stair[1] + stair[2])</code></pre><p>dp 시작부분입니다. 0과 1, 2는 초기값을 지정해줍시다. 이것 또한 마찬가지로 점화식에서 나오는 최소 변수가 N-3부터이기 때문에 4번째 계단부터 실질적으로 점화식 사용이 가능하기 때문입니다. </p>
<pre><code>    #dp 점화식
    for i in range(3,N):
        dp[i] = max(dp[i-3]+stair[i-1]+stair[i],dp[i-2]+stair[i])
</code></pre><p>위에 문제분석을 실시한대로 </p>
<p>N-3,직전계단,마지막계단을 거쳐서 오는가 
직전계단,마지막계단을 거쳐서 오는가</p>
<p>를 판단해주어 그중 더 큰 값을 갖는 값을 dp리스트에 담아주도록 합시다. </p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/55bdb6d6-0778-4628-ab25-c8065edc756f/image.png" alt="">
그러면 dp값은 결국 해당 계단 위치에서 가질 수 있는 최댓값의 모임이 되고 
그중 제일 마지막 계단의 최댓값 =&gt; 결국 정답이니까</p>
<pre><code>    print(dp[N-1])</code></pre><p>을 해주면 성공적으로 정답을 도출할 수 있습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/43e4273d-a127-486e-99b2-cf0666a5a2b6/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/40a24cad-8ce1-44d1-b586-a667edeba999/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.23 오늘의 코딩공부]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.22-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.22-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%BD%94%EB%94%A9%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Tue, 23 Jan 2024 05:16:31 GMT</pubDate>
            <description><![CDATA[<p>그동안 BFS와 DFS 골드 문제들을 풀어보면서 알고리즘이 어떤 건지, 어떻게 활용하여 해결 할 수 있는지 감을 잡아갔습니다. </p>
<p>약 47일간 DFS와 BFS를 해결했으므로 하나만 계속해서 힘든 것도 있고 보다 다양한 알고리즘을 접하고자 오늘부터는 DP 알고리즘에 손대보려고 합니다.</p>
<hr>
<h1 id="dp란">DP란?</h1>
<p>Dynamic Programming의 약자입니다. </p>
<pre><code>def dp(N):
    if(N==1): return 1
    if(N==2): return 1
    return dp(N-1) + dp(N-2)
print(dp(10))</code></pre><p>여기 피보나치 수열을 구하는 식이 있습니다. 
이렇게 코딩을 작성하고 실행해보면 
<img src="https://velog.velcdn.com/images/ghost_dog/post/df68289b-ae09-4ca8-9f2e-3743014b9517/image.JPG" alt="">
이러한 형식을 거쳐 재귀적으로 실행되겠죠. 
그런데 위 실행 형식을 자세히 들여다보면 
<img src="https://velog.velcdn.com/images/ghost_dog/post/4a5c429f-3574-44b5-8197-277cc0f30fe5/image.JPG" alt="">
dp(8)도 두번 쓰이고
<img src="https://velog.velcdn.com/images/ghost_dog/post/2ed1d68b-92f9-41fb-863d-fced2a7a7101/image.JPG" alt="">
dp(7)도 두번 쓰이니 효율이 굉장히 떨어진다는 생각이 들지 않나요? 
실제로 컴퓨터에서 위 코드 컴파일을 돌려보면 
<img src="https://velog.velcdn.com/images/ghost_dog/post/b826e69f-e666-488a-bf84-8847bbf08571/image.png" alt=""></p>
<p>dp(10)같은 경우는 55로 잘 나오는데 
<img src="https://velog.velcdn.com/images/ghost_dog/post/c83d7a78-1442-4134-8ab5-31b6b6410915/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/3281b065-88cf-4a9e-b554-13b42cada78b/image.png" alt="">
dp(50)같은 경우는 때려죽여도 안나옵니다. </p>
<p>근데 이건 당연한 현상입니다. 위 코드대로 dp(50)을 구할 경우엔 1,000,000 초 = 약 277시간 후에나 컴파일 값이 나올 겁니다. </p>
<p>이렇게 쓸모없는 더미 자료들(위 예시에서의 dp(8),dp(7) 등)을 줄이고 시간복잡도를 2의 n승에서 O(n)으로 획기적이게 줄일 수 있는 알고리즘이 바로 DP의 메모이제이션입니다. </p>
<p>쉽게 설명하면 저 더미 자료들을 배열 하나 만들어서 저장하고, 만약에 현재 조사중인 N이 배열 안에 있는 자료라면 그대로 갖다 써먹는 방식입니다. </p>
<pre><code>memo = [0] * 100
def dp(N):
    if(N==1): return 1
    if(N==2): return 1
    if(memo[N] != 0): return memo[N]
    memo[N] = dp(N-1) + dp(N-2)
    return memo[N]
print(dp(55))</code></pre><p>이런 방식으로 배열 하나 만들어주고 메모가 비어있지 않으면 해당 메모로  return  해줍니다. 
그리고 함수를 실행할 때마다 memo의 n번째 
인덱스에 해당하는 값을 넣어줍니다. 
위 식을 실행하면
<img src="https://velog.velcdn.com/images/ghost_dog/post/297c7cc1-76b7-4200-bf1c-b5babdd5a9cd/image.png" alt=""></p>
<p>이런씩으로 시간복잡도가 크게 줄어 금방 컴파일 값을 도출할 수 있습니다. 
<img src="https://velog.velcdn.com/images/ghost_dog/post/ef350e52-861f-4d2f-840b-d49eaf9cc46d/image.JPG" alt=""></p>
<hr>
<blockquote>
<p><strong>1003번 - 피보나치 함수</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/87a5ec01-4047-4beb-bdcc-244e95597cc0/image.png" alt=""></p>
</blockquote>
<p>본격적으로 문제풀이를 해보겠습니다. 
피보나치 수열에서 N을 호출할 때 0과 1이 각각 몇번 불러와지는지 횟수를 구하랍니다. </p>
<pre><code>T = int(input())
for _ in range(T):
    zero = 0
    one = 0
    memo = [0]*41
    a = int(input())
    fib(a)</code></pre><p>먼저 선언을 해주고 메모이제이션을 위한 memo의 길이는 n&lt;41이므로 41로 설정하겠습니다.</p>
<pre><code>    if a == 0:
        print(1,0)
    elif a == 1:
        print(0,1)
    elif a == 2:
        print(1,1)
    else:
        print(memo[a-1],memo[a])</code></pre><p>DP는 점화식을 세워주는게 제일 중요한데 위와 같은 예외의 경우가 아니면 1과 0의 호출횟수를 메모이제이션을 통해 알아낼 수 있습니다.</p>
<pre><code>def fib(N):
    if (N == 0): 
        return 0
    if (N == 1):
        return 1
    if (memo[N] != 0):
        return memo[N]
    memo[N] = fib(N-1) + fib(N-2)
    return memo[N]</code></pre><p>메모이제이션을 사용하여 메모에 담겨있으면 return memo[N] 형식으로 return 해주고 메모에 담겨있지 않다면 memo[n]을 fib(n-1) + fib(n-2)로 지정해줍시다. </p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/9d78c3ce-742d-4ce4-9b3f-e77fb48f9b97/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/bef74b57-5fc6-4b3d-8dd5-ce090fdb7aa1/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👩‍🎓공부 - 2024.01.22 오늘의 백준 문제풀이
]]></title>
            <link>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.22-%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-qqxqsgaj</link>
            <guid>https://velog.io/@ghost_dog/%EA%B3%B5%EB%B6%80-2024.01.22-%EC%98%A4%EB%8A%98%EC%9D%98-%EB%B0%B1%EC%A4%80-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-qqxqsgaj</guid>
            <pubDate>Mon, 22 Jan 2024 07:48:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>13265번 - 색칠하기</strong>
<img src="https://velog.velcdn.com/images/ghost_dog/post/f06e4868-c8ff-4cb3-8511-01df04c488fd/image.png" alt=""></p>
</blockquote>
<p>트리가 하나 주어졌을 때 이 트리를 두 가지 색으로 색칠할 수 있는지 묻는 문제입니다. 
다만, 이어진 노드끼리는 같은 색을 띨 수 없습니다. </p>
<hr>
<h1 id="접근">접근</h1>
<p>일단 단순히 구현 문제라고 생각하고 
노드를 모두 이진 트리로써 취급하여 생각해보기로 했습니다. 
레벨 1에서는 빨간색, 레벨 2에서는 흰색, 레벨 3에서는 다시 빨간색 ... 이런씩으로 하여 
만약 현재 탐색중인 노드와 탐색할 노드가 서로 이어져있는데 같은 색을 띈다, 이러면 두 가지 색으로 칠하지 못하고 만약 아니라면 두 가지 색으로 칠할 수 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/0f6287de-c1c4-41e5-80d6-1d42f8a01a25/image.JPG" alt=""></p>
<p>아마 그림으로 그려보면 이런 느낌이지 않을까 싶습니다...</p>
<hr>
<h1 id="선언">선언</h1>
<hr>
<pre><code>t = int(input())
que = deque()

for _ in range(t):
    ans = []
    que.clear()
    a,b = map(int,input().split())</code></pre><p>일단 트리를 t만큼 받고 BFS 사용을 위해 que를 받아줍니다. 
그리고 a,b로 트리를 받아주기 시작하겠습니다. que를 비우는 이유는 이따 상세히 서술하겠습니다. </p>
<pre><code>    graph = [[0]*(a+1) for _ in range(a+1)]
    visited = [0]*(a+1)</code></pre><p>그래프와 방문여부를 선언하고 </p>
<pre><code>    for _ in range(b):
        c,d = map(int,input().split())
        graph[c][d] = graph[d][c] = 1
</code></pre><p>이차원배열로 c노드와 d노드가 이어졌다는 형식으로 표현해줍시다. </p>
<pre><code>    for i in range(1,d+1):
        if not visited[i]:
            ans.append(bfs(i))</code></pre><p>그리고 노드중에 방문 안한 노드가 존재한다면 해당 노드는 bfs 탐색을 돌려줍니다. </p>
<hr>
<h1 id="bfs">BFS</h1>
<pre><code>from collections import deque
import sys
input = sys.stdin.readline


def bfs(N):
    color = True
    visited[N] = 1
    graph[0][N] = color
    color = False
    que.append(N)
</code></pre><p>이 문제가 풀다보니 시간제한에 좀 민감한 친구여서 sysinput 선언해주고 시작하겠습니다. 
어짜피 색은 두가지므로 True 와 False로 구분해주고 초기 색깔은 True로 지정해준 다음에 초기 색깔을 graph[0]에 넣어주겠습니다 (어짜피 안쓰는 자리, 형식상 인덱스 숫자 맞추려고 +1 선언해서 비는 공간). 그리고 노드 레벨 0은 탐색이 끝난거니까 color는 False로 바꿔주고 (다음 트리 레벨 넘어 갈 때 색 변경) 방문여부 1로 바꿔주고 que에 추가하면서 탐색을 시작합니다. </p>
<pre><code>    while que:
        for _ in range(len(que)):
            connect = que.popleft()
            for i in range(len(graph[0])):</code></pre><p>이번에도 이전 문제와 마찬가지로 BFS의 level을 파악해야 하므로 len(que)만큼만 for문을 돌려주겠습니다. 그리고 항상 탐색하듯이 </p>
<pre><code>                if not visited[i] and graph[connect][i] != 0:
                    visited[i] = 1
                    que.append(i)</code></pre><p>만약 노드끼리 이어져있고 아직 방문을 안했으면 방문처리 하고 다음 탐색구간으로 지정해줍니다. </p>
<pre><code>                    graph[0][i] = color</code></pre><p>초반에 레벨 0에서 컬러를 바꾸고 for문에 들어왔으므로 color 변수 그대로 graph[0][i], 즉 다음 탐색지점의 색깔을 대입해주도록 합니다. </p>
<pre><code>                if graph[connect][i] != 0:
                    if graph[0][i] == graph[0][connect]:
                        return &#39;impossible&#39;</code></pre><p>만약 탐색을 할 노드와 현재 탐색 중인 노드의 색깔이 일치한다면 impossible을 return 해주도록 합시다.
0이 아닐 때라는 조건을 건 이유는 만약 graph[0]의 인덱스의 초기값이 0인데 색깔이 아예 안들어있는 인덱스와 맞물리게 되면 조건식이 발동되는 대참사가 일어나므로 지정해줘야 합니다. </p>
<pre><code>        if color == False:
            color = True
        elif color == True:
            color = False
</code></pre><p>마지막으로 해당 레벨의 색깔을 다 지정했으면 다음 레벨로 넘어갈 때 색깔을 변경해줍시다. </p>
<hr>
<h1 id="출력">출력</h1>
<pre><code>    if &#39;impossible&#39; in ans:
        print(&#39;impossible&#39;)
    else:
        print(&#39;possible&#39;)</code></pre><p>최종적으로 ans배열에 impossible이 있다면 불가능, 없다면 가능을 출력해주면 끝납니다. </p>
<hr>
<p><img src="https://velog.velcdn.com/images/ghost_dog/post/85d0ac3c-15fb-4c88-93f7-51061ea341fc/image.png" alt="">
<img src="https://velog.velcdn.com/images/ghost_dog/post/a70027b8-a449-4feb-acfd-28004b00b1dd/image.png" alt=""></p>
<hr>
<h1 id="중요-실수">중요 실수</h1>
<p>아까</p>
<pre><code>for _ in range(t):
    ans = []
    que.clear()
    a,b = map(int,input().split())</code></pre><p>에서 que.clear()를 해준 이유가 이게 BFS가 실행되다가 return을 만나면 굳이 while에 있는 que값을 다 빼고 나올 필요가 없기 때문에 que에 값이 잔류하게 됩니다. </p>
<p>그래서 이 상태로 que를 초기화시키지 않고 다시 BFS를 돌리면 값이 오염되서 이상한 결과가 나와버립니다. 이것때문에 상당히 시간을 허비해서 앞으로 코드를 짤 때는 이런 변수들 유심히 살펴야겠습니다. </p>
]]></description>
        </item>
    </channel>
</rss>