<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Simple Explain</title>
    <link>https://theconcentrationoftime.tistory.com/</link>
    <description>Truly believed that if you couldn't explain something simply, you didn't understand it.</description>
    <language>ko</language>
    <pubDate>Thu, 9 Apr 2026 20:20:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>winney916</managingEditor>
    <image>
      <title>Simple Explain</title>
      <url>https://tistory1.daumcdn.net/tistory/4645886/attach/f556822bfffa4f3a9de8796dc5fbb7b0</url>
      <link>https://theconcentrationoftime.tistory.com</link>
    </image>
    <item>
      <title>프로그래머스 연습문제 : 정수 제곱근 판별</title>
      <link>https://theconcentrationoftime.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12934#&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/12934#&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1729734520187&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12934#&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2tInw/hyXpx5OW2c/oKk0NmOYjv9KIoCM5fl9T1/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/baE4m6/hyXprxLngO/uawt1wNHxbvmTqkmrmEBlk/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12934#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12934#&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2tInw/hyXpx5OW2c/oKk0NmOYjv9KIoCM5fl9T1/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/baE4m6/hyXprxLngO/uawt1wNHxbvmTqkmrmEBlk/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음.. 쉬운 문제라고 생각하고 풀었는데, 채점 케이스에서 마지막 케이스가 오류가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 이런 경우는 극단값을 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;틀린 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1729734557601&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(n):
    answer = -1
    for i in range(1, n//2):
        # print(i)
        if i**2==n:
            return (i+1)**2
    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 생각해볼 수 있는 극단값은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값 n의 범위는 1부터 50000000000000까지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1을 넣으면, 1은 1의 제곱이기 때문에 (1+1)의 제곱인 4가 출력되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위의 코드에서는 1을 넣을 경우 for문이 돌아가지 않고 -1이 리턴된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 1이 들어간 경우에도 for문이 실행될 수 있도록 range를 조절해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1729734653466&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(n):
    answer = -1
    for i in range(1, n//2+2):
        # print(i)
        if i**2==n:
            return (i+1)**2
    return answer

&quot;&quot;&quot;
채점 마지막 케이스 오류
-&amp;gt; 극단값 조사 : n=1, return 4
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;항상 극단값을 고려하자. 입력값의 범위를 기억할 것&quot;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Algorithm/programmers.co.kr</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/170</guid>
      <comments>https://theconcentrationoftime.tistory.com/170#entry170comment</comments>
      <pubDate>Thu, 24 Oct 2024 10:51:20 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 고득점 kit #DP : N으로 표현</title>
      <link>https://theconcentrationoftime.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42895&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42895&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1729676342473&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42895&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTZ6ln/hyXlUnTEsf/nRhXo9bgXiL8BKgYSO6WkK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/c2BYmo/hyXlN3lxmF/r0rFsWkdD71AmQUTvvOiL0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42895&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42895&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTZ6ln/hyXlUnTEsf/nRhXo9bgXiL8BKgYSO6WkK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/c2BYmo/hyXlN3lxmF/r0rFsWkdD71AmQUTvvOiL0/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직도 DP가 조금 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 값을 기준으로 dp 를 만들어야할지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp가 1차원일지 2차원일지 판단하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도, 모든 경우의 수를 수기로 써내려가며 점화식을 찾는 연습이 부족한 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1729676380811&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(N, number):
    answer = -1
    # dp를 무엇으로 기준으로 잡냐에 따라서 문제를 풀 수 있고 없고가 갈린다.
    # 보통 dp에서는 범위가 작게 나온 것이 기준이 되는 경우가 많다.
    # &quot;최솟값이 8보다 크면 -1을 return 합니다.&quot;라는 조건이 있다.
    # 즉, 숫자를 최대 8개까지만 사용가능하는 것이다.
    # 이게 이 문제의 핵심이다.
    dp = [set() for _ in range(9)] # 1~8 인덱스 사용

    for i in range(1, 9):
        tmp = int(str(N)*i)
        dp[i].add(tmp)
        for j in range(0, i):
            for item1 in dp[j]:
                for item2 in dp[i-j]:
                    added = item1+item2
                    dp[i].add(added)
                    minused = item1-item2
                    dp[i].add(minused)
                    multied = item1*item2
                    dp[i].add(multied)
                    if item2&amp;gt;0:
                        divided = item1//item2
                        dp[i].add(divided)
        if number in dp[i]:
            return i
    return answer

&quot;&quot;&quot;
연산 방법 정리 (5가지) : 덧셈, 뺄셈, 나눗셈, 곰셈, 숫자 붙여쓰기
ex : 5
1개 : 5
2개 : 5+5, 5-5, 5/5, 5*5, 55
3개 :
    (5+5)+5=15, (5+5)-5=5, (5+5)/5=2, (5+5)*5=50 | (5+5)5=105, 이런건 안되는 듯
    (5-5)+5=6, (5-5)-5=-4, (5-5)/5=1/5, (5-5)*5=5
    5/5+5, 5/5-5, 5/5/5, 5/5*5
    5*5+5, 5*5-5, 5*5/5, 5*5*5
    55+5, 55-5, 55/5, 55*5
-&amp;gt; 따라서 점화식은 dp[i]의 원소 = dp[i-j]의 원소 (사칙연산) dp[j]의 원소

&quot;&quot;&quot;
# dp가 어려우면 2차원으로 빠진다는 것을 명심하라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/programmers.co.kr</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/169</guid>
      <comments>https://theconcentrationoftime.tistory.com/169#entry169comment</comments>
      <pubDate>Wed, 23 Oct 2024 18:39:44 +0900</pubDate>
    </item>
    <item>
      <title>어렵다어렵다 하면 어렵고 쉽다 쉽다 하면 쉽다 (#1865 웜홀)</title>
      <link>https://theconcentrationoftime.tistory.com/168</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#1865 웜홀 (&lt;a href=&quot;https://www.acmicpc.net/problem/1865&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1865&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 사용하는 알고리즘이었던지라 많이 애먹었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라는 좀 써봤지만 벨만 포드는 처음인지라..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 너무 어려워보여서 이런 저런 조건들을 마구 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 생각보다 풀이가 심플해서 놀랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레티넘을 풀다보면 꼭 뇌가 이상하게 꼬인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 골드까지는 그런 꼬임이 필요하지 않다는 것을 꼭 기억하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1729058245656&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

input = sys.stdin.readline

INF = int(1e9)
tc = int(input())


def bf():
    # 벨만 포드 : 매 단계마다 모든 간선을 전부 확인하면서 모든 노드간의 최단 거리를 구해
    # 모든 라운드 반복
    for i in range(n):
        # 모든 간선 탐색
        for j in range(m * 2 + w):
            start = loads[j][0]
            dest = loads[j][1]
            cost = loads[j][2]
            # print(loads[i])
            if distance[dest] &amp;gt; distance[start] + cost:
                distance[dest] = distance[start] + cost
                # 마지막 라운드에서도 탐색이 발생한다면
                if i == n - 1:
                    # 음수 사이클이 존재하는 것
                    return True
    return False


for _ in range(tc):
    n, m, w = map(int, input().split())
    # 지점의 수 N(1 &amp;le; N &amp;le; 500), 도로의 개수 M(1 &amp;le; M &amp;le; 2500), 웜홀의 개수 W(1 &amp;le; W &amp;le; 200)

    # cycle을 만드는 최단거리를 저장하기 위한 리스트
    distance = [INF] * (n + 1)  # 1번부터 사용

    loads = []

    # 도로는 방향이 없으며 웜홀은 방향이 있다
    for i in range(m):  # 도로의 정보
        s, e, t = map(int, input().split())
        # S와 E는 연결된 지점의 번호, T는 이 도로를 통해 이동하는데 걸리는 시간
        loads.append([s, e, t])  # [start, dest, cost]
        loads.append([e, s, t])  # 양방향

    for j in range(w):  # 웜홀의 정보
        s, e, t = map(int, input().split())
        # S는 시작 지점, E는 도착 지점, T는 줄어드는 시간, T는 10,000보다 작거나 같은 자연수 또는 0
        loads.append([s, e, -t])  # 웜홀은 단방향

    if bf():
        print(&quot;YES&quot;)
    else:
        print(&quot;NO&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다익스트라 vs 벨만 포드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 최단거리를 구하는 알고리즘이다. 하지만 음수 간선이 있는지의 여부에 따라 사용하는 알고리즘이 갈린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 음수 간선이 있어도 다익스트라로 최단거리를 구할 수는 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문제는, 사이클이 음수 간선을 포함한 경우에 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQRrvl/btsJ6ZQZ84D/PNPAWr11ZRIPEUKtANgwC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQRrvl/btsJ6ZQZ84D/PNPAWr11ZRIPEUKtANgwC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQRrvl/btsJ6ZQZ84D/PNPAWr11ZRIPEUKtANgwC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQRrvl%2FbtsJ6ZQZ84D%2FPNPAWr11ZRIPEUKtANgwC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;172&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 그래프를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-3-5 사이클을 돌게되면 비용이 -1이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 어떤 경로를 가더라도 이 사이클을 계속해서 돌아버리면, 비용은 음의 무한대가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 음수 간선이 포함된 상황에서는 벨만 포드 알고리즘을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 음수 간선이 없는 상황에서도 벨만 포드 알고리즘은 작동한다. 하지만 시간 복잡도 측면에서 다익스트라가 더 효율적이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다익스트라&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택&lt;/li&gt;
&lt;li&gt;음수 간선이 없다면 최적의 해를 찾을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;벨만 포드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;음의 간선이 포함된 상황에서도 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;또한, 음수 간선의 순환을 감지할 수 있다.&lt;/li&gt;
&lt;li&gt;하지만 시간복잡도가 O(V*E)로 다익스트라에 비해 느리다. (V는 노드의 수, E는 간선의 수)&lt;/li&gt;
&lt;li&gt;매번 모든 간선을 전부 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 다익스트라 알고리즘의 최적의 해를 항상 포함함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다익스트라에 비해 시간이 오래걸리지만, 음수 간선 순환을 파악할 수 있다는 장점이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행 과정은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;출발 노드 설정&lt;/li&gt;
&lt;li&gt;최단 거리 테이블 초기화&lt;/li&gt;
&lt;li&gt;다음의 과정을 N-1번 반복
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전체 간선 E개를 하나씩 확인&lt;/li&gt;
&lt;li&gt;각 간선을 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 테이블을 갱신&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;만약 음수 간선 순환이 발생하는지 체크하고 싶다면, 3번의 과정을 한 번 더 수행하면 됨
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이 때 최단 거리 테이블이 갱신된다면 음수 간선 순환이 존재하는 것.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ppimbaxm8d8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Ppimbaxm8d8&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Ppimbaxm8d8&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/dApn4V/hyXhRdFC9I/Y5SkhhlBKDW3QR01oAPKo0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/een253/hyXhL5COxC/yrmgAh4tDChaeA2DSdkVVk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;코딩 테스트를 위한 벨만 포드 알고리즘 7분 핵심 요약&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Ppimbaxm8d8&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>1865</category>
      <category>벨만포드</category>
      <category>웜홀</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/168</guid>
      <comments>https://theconcentrationoftime.tistory.com/168#entry168comment</comments>
      <pubDate>Wed, 16 Oct 2024 14:58:55 +0900</pubDate>
    </item>
    <item>
      <title>실버는 선녀야 (#13305 주유소)</title>
      <link>https://theconcentrationoftime.tistory.com/167</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#13305 주유소 &lt;a href=&quot;https://www.acmicpc.net/problem/13305&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/13305&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 기하학 플래티넘 문제를 풀고 멘탈이 나가있던 상황에서, 오늘은 이 문제를 풀게됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 쉬웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리디 알고리즘인 만큼 논리적으로 최적해를 찾아내면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728965436863&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input())
distance = [int(x) for x in input().split()]
oil_cost = [int(x) for x in input().split()]

answer = 0
i = 0
while i &amp;lt; n:
    now = oil_cost[i]

    for j in range(i + 1, n):
        next = oil_cost[j]
        if next &amp;lt; now:
            break
    else:  # 현재 위치보다 작은게 없는 경우
        answer += now * sum(distance[i:])
        break
    next = j
    answer += sum(distance[i:j]) * now
    i = j

print(answer)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>13305</category>
      <category>그리디</category>
      <category>주유소</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/167</guid>
      <comments>https://theconcentrationoftime.tistory.com/167#entry167comment</comments>
      <pubDate>Tue, 15 Oct 2024 13:10:45 +0900</pubDate>
    </item>
    <item>
      <title>아니 이걸 어떻게 풀어 (#1708 볼록 껍질)</title>
      <link>https://theconcentrationoftime.tistory.com/166</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#1708 볼록 껍질 &lt;a href=&quot;https://www.acmicpc.net/problem/1708&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1708&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 말도안되는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄청 복잡한 논리적 구조로 접근했는데, 그냥 개념을 알아야 풀 수 있는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 개념은 다음 두 가지이다. (기하학 문제이다 보니..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Counter Clockwise&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 점 a, b, c가 있을 때, a를 기준으로 b, c가 어느 방향으로 위치하는지를 검사하는 공식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 간단하다. a를 기준으로 두 개의 벡터를 만든다. ab, ac&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;두 벡터를 외적(Cross Products)&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외적은 특이한 성질이 있는데,&amp;nbsp;&lt;br /&gt;V X W를 진행했을 때, 결과값이 양수라면 -&amp;gt; V벡터가 W벡터의 오른쪽(시계방향)에 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 결과값이 음수라면 V벡터가 W벡터의 왼쪽(반시계방향)에 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 영상을 통해 이 개념을 확립할 수 있다. (한글 자막 지원)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=eu6i7WJeinw&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=eu6i7WJeinw&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=eu6i7WJeinw&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/dTtoOz/hyXhNhIljb/KIyLYtBCJJSI0qwNgpxIVK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/b6WHNe/hyXhWFIbIS/7k0m3X28NMketWa5vziTQK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Cross products | Chapter 10, Essence of linear algebra&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/eu6i7WJeinw&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, ab, ac 두 벡터의 외적의 결과값이 음수인 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 ab는 벡터 ac의 왼쪽(반시계방향)에 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 점b는 점c의 반시계방향에 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이러한 외적의 성질을 바탕으로 반시계방향으로 존재하는지를 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Graham Scan (그레이엄 스캔)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 풀이하는 실질적인 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/bloodsoda/221029163536&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/bloodsoda/221029163536&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728964644944&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[알고리즘(Algorithm)] 컨벡스 헐 알고리즘(Convex Hull) - graham scan&quot; data-og-description=&quot;컨벡스 헐 알고리즘 중, graham scan 방법이다. 1. y좌표값이 가장 작은 점을 고른다. y좌표값이 가장 작은...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/bloodsoda/221029163536&quot; data-og-url=&quot;https://blog.naver.com/bloodsoda/221029163536&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cwLUzQ/hyXhQS2nDO/iRpfDudYYLMO7QuFb1HPTK/img.png?width=645&amp;amp;height=700&amp;amp;face=0_0_645_700&quot;&gt;&lt;a href=&quot;https://blog.naver.com/bloodsoda/221029163536&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/bloodsoda/221029163536&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cwLUzQ/hyXhQS2nDO/iRpfDudYYLMO7QuFb1HPTK/img.png?width=645&amp;amp;height=700&amp;amp;face=0_0_645_700');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[알고리즘(Algorithm)] 컨벡스 헐 알고리즘(Convex Hull) - graham scan&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;컨벡스 헐 알고리즘 중, graham scan 방법이다. 1. y좌표값이 가장 작은 점을 고른다. y좌표값이 가장 작은...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 블로그에 잘 정리되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 문제는 다음과 같은 순서로 풀이할 수 있다. (그레이엄 스캔 구현)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 전체 점 중에서 가장 아래 가장 왼쪽에 있는 점을 찾는다. (y, x 순으로 key를 주어 정렬한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이 점을 제외한 나머지 모든 점들을 Couter ClockWise 공식을 사용해서 정렬한다. (반시계 방향으로 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정이 조금 까다롭다. 나의 경우에는 python을 사용했는데, key에 특정한 함수를 넣어서 원활히 정렬되도록 구현해야만 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업이 끝났을 때, 점의 좌표가 저장된 리스트의 0번 인덱스에는 (1)에서 찾은 가장 아래, 가장 왼쪽에 있는 점이 있어야하고, 1번 인덱스 부터는 반시계방향으로 정렬된 형태여야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. stack에 위에서 정렬한 점들 중 0번과 1번 점을 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.&amp;nbsp;이제 준비는 끝났다. 다음 세 가지 순서를 반복한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-1. 2번 인덱스의 점부터 순차적으로 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-2-1. 스택 최상단에 있는 두 점을 이은 직선에 대해, 현재 탐색중인 점이 왼쪽(반시계)에 존재한다면 해당 점을 stack에 push 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-2-2. 만약 그렇지 않다면, stack의 최상단에 있는 값을 버리고, 다시 조건을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 위 과정이 끝나면, stack에는 convex hull을 구성하는 점들만 남아있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하...&amp;nbsp; 진짜 너무 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 고민하고 풀어내길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728965109504&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from functools import cmp_to_key

N = int(input().strip())
dots = []
# 1. 입력 받으면서 최대, 최소 좌표를 찾기  : O(n)
for i in range(N):
    x, y = map(int, input().split())
    dots.append([x, y])


def ccw(a, b, c):  # Counter Clockwise 검사.
    # 세 점의 방향을 시계방향인지, 반시계방향인지, 일직선인지 판별하는 수학적 테크닉이다.
    # 벡터의 외적 값을 계산해서 이를 판별할 수 있다.
    # 결과값이 음수인 경우, 반시계방향, 양수인 경우 시계방향, 0인 경우 1직선상에 위치한 점이라는 것이다.
    return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])


def dist(a, b):  # 점간 거리
    return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2


# 반시계방향 정렬 함수
def comp(a, b):
    is_clockwise = ccw(pivot, a, b)
    # 1. 두 점 모두 dots[0]과 직선 상에 있으면 가까운 점을 먼저 정렬한다.
    if is_clockwise == 0:
        return dist(pivot, a) &amp;lt; dist(pivot, b)
    # 2. 그게 아니라면,
    else:
        # 음수일 때, 시계방향이기 때문에 음수인 상황을 False로 반환해야 앞쪽으로 배치된다.
        return -1 if is_clockwise &amp;gt; 0 else 1


# Graham Scan의 동작 과정은 다음과 같다.
# 1. 주어진 점들을 반시계방향으로 정렬하고, 정렬된 순서대로 점들을 탐색한다.
# 1-1. Y좌표 기준으로 정렬
dots.sort(key=lambda x: (x[1], x[0]))

pivot = dots.pop(0)
# 1-2. 가장 왼쪽 점을 기준으로 반시계방향 정렬
dots.sort(key=cmp_to_key(comp))
# python sort key에 true, false를 반환하는 함수가 들어간다면, false(0)일 때 앞으로, true(1)일 때 뒤로 간다.
dots.insert(0, pivot)

# 2. stack에 1번 점과 2번 점을 push한다.
stack = [0, 1]
# 3. 3번 ~ N번째 점까지 아래의 과정을 반복할 것이다.
for i in range(2, N):  # i는 현재 탐색하고 있는 점의 index
    # 만약 stack의 최상단에 있는 두 점을 이은 직선 l에 대해, 현재 탐색하는 점이 l의 왼쪽에 존재한다면 stack에 push한다.
    while len(stack) &amp;gt;= 2 and ccw(dots[i], dots[stack[-2]], dots[stack[-1]]) &amp;lt;= 0:
        # 그렇지 않다면 stack을 1번 pop하고 위 조건을 다시 확인한다.
        stack.pop()
    stack.append(i)
# 4. 최종적으로 탐색이 끝나면 stack에는 Convex Hull을 구성하는 점들이 포함되어 있다.

print(len(stack))

# 새로 배운 것.
# Counter Clockwise
# Graham Scan&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 참, 파이썬에서 cmp함수를 사용해서 정렬하는 방법도 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 sort함수는 기본적으로 정렬의 기준을 삼을 key를 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, cmp함수를 바로 적용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;functools에서 cmp_to_key를 받아와 적용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmp는 두 입력값을 비교해서 순서를 매겨주는 함수이기 때문에... key와는 다른 적용 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>1708</category>
      <category>convex hull</category>
      <category>기하학</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/166</guid>
      <comments>https://theconcentrationoftime.tistory.com/166#entry166comment</comments>
      <pubDate>Tue, 15 Oct 2024 13:06:44 +0900</pubDate>
    </item>
    <item>
      <title>이제는 플레를 향해 (#11375)</title>
      <link>https://theconcentrationoftime.tistory.com/165</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#11375 열혈강호 &lt;a href=&quot;https://www.acmicpc.net/problem/11375&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11375&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 열심히 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;틀린 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728815140851&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n, m = map(int, input().split())
people = {x: [] for x in range(1, n + 1)}
for i in range(1, n + 1):
    tmp = list(map(int, input().split()))
    if tmp[0]:
        people[i] = tmp[1:]

works = [0] * (m + 1)  # 인덱스 1부터 사용
# print(people)


def allocate(idx):
    # input()
    target = people[idx]
    if len(target):
        # 1. 가장 첫 번째 일에 연결
        for w in target:
            # hidden
            if works[w] == idx:
                continue
            # print(idx, w)
            if works[w] == 0:  # 2-1. 연결되지 않은 상태일 경우
                works[w] = idx  # 2-1-1. 현재 작업자를 연결
                return  # 2-2-2. 연결 성공 시 종료
            else:  # 2-2. 연결된 상태일 경우
                prev = works[w]
                allocate(prev)  # 2-2-1. 기존의 작업자를 이동
                if works[w]:  # 2-2-2. 기존 작업자의 이동이 불가한 경우
                    continue  # 2-2-2-1. 현재 작업자의 다음 일로, 2번부터 진행
                else:  # 2-2-3. 기존 작업자의 이동이 가능한 경우
                    works[w] = idx  # 2-2-3-1. 연결
                    return  # 2-2-3-2. 연결 성공 시 종료
        # 3. 이를 반복하고, 할당 가능한 일이 없는 경우, 포기. 현재 작업자를 버림.


for i in range(1, n + 1):
    allocate(i)
# print(people)
print(sum(map(lambda x: x &amp;gt; 0, works)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리적 사고 흐름을 그대로 옮겨 구현한 방식인데, 재귀 호출 횟수가 너무 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 분류를 보니 &quot;이분 매칭&quot;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음... 이분 탐색도 아니고 이분 매칭이라니...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 모르는 알고리즘이 나오기 시작한다. 이게 플레티넘?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열심히 학습했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 단순해서 정말 놀랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 이해하는 데 있어서 조금 버거웠지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해해보니 DFS와 크게 다르지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yjg-lab.tistory.com/209&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yjg-lab.tistory.com/209&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728815335919&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[알고리즘] 이분 매칭 알고리즘 (Bipartite Matching)&quot; data-og-description=&quot;이분 매칭 알고리즘 (Bipartite Matching) 두 개의 정점 그룹이 존재할 때 모든 간선(경로)의 용량이 1이면서 양쪽 정점이 서로 다른 그룹에 속하는 그래프를 이분 그래프(Bipartite Graph)라고 말합니다. &quot; data-og-host=&quot;yjg-lab.tistory.com&quot; data-og-source-url=&quot;https://yjg-lab.tistory.com/209&quot; data-og-url=&quot;https://yjg-lab.tistory.com/209&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qg2EN/hyXd5X6IPh/P6wJ4jEtKuqy1DJlF5YoZK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/GX02m/hyXd30ixLM/ovIhMkn1Be3sw4NvDvTJU0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mm755/hyXegSTell/mhysvZyyVgLofgQoItZzbK/img.png?width=1106&amp;amp;height=639&amp;amp;face=0_0_1106_639&quot;&gt;&lt;a href=&quot;https://yjg-lab.tistory.com/209&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yjg-lab.tistory.com/209&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qg2EN/hyXd5X6IPh/P6wJ4jEtKuqy1DJlF5YoZK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/GX02m/hyXd30ixLM/ovIhMkn1Be3sw4NvDvTJU0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/mm755/hyXegSTell/mhysvZyyVgLofgQoItZzbK/img.png?width=1106&amp;amp;height=639&amp;amp;face=0_0_1106_639');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[알고리즘] 이분 매칭 알고리즘 (Bipartite Matching)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이분 매칭 알고리즘 (Bipartite Matching) 두 개의 정점 그룹이 존재할 때 모든 간선(경로)의 용량이 1이면서 양쪽 정점이 서로 다른 그룹에 속하는 그래프를 이분 그래프(Bipartite Graph)라고 말합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yjg-lab.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728815347575&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

sys.setrecursionlimit(1000 * 1000)

input = sys.stdin.readline

n, m = map(int, input().split())
people = {x: [] for x in range(1, n + 1)}
for i in range(1, n + 1):
    tmp = list(map(int, input().split()))
    if tmp[0]:
        people[i] = tmp[1:]

visited = [False] * (m + 1)  # 인덱스 1부터 사용. 방문기록
workers = [0] * (m + 1)  # 인덱스 1부터 사용. 각 인덱스를 누가 처리하기로 했는지를 기록
# print(people)


def allocate(idx):
    for i in range(len(people[idx])):  # 가능한 일을 모두 순회
        now = people[idx][i]
        if visited[now]:  # 방문한 일이면 무시
            continue
        else:  # 방문하지 않은 일이면 진행
            visited[now] = True  # 방문처리
            if workers[now] == 0 or allocate(workers[now]):
                workers[now] = idx
                return True
    return False


for idx in range(1, n + 1):
    visited = [False] * (m + 1)
    if n == sum(map(lambda x: x &amp;gt; 0, workers)):
        print(n)
        exit()
    else:
        allocate(idx)
# print(people)
print(sum(map(lambda x: x &amp;gt; 0, workers)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이분 매칭 문제는 다 풀 수 있을 것 같다.&lt;/p&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>11375</category>
      <category>Python</category>
      <category>이분매칭</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/165</guid>
      <comments>https://theconcentrationoftime.tistory.com/165#entry165comment</comments>
      <pubDate>Sun, 13 Oct 2024 19:29:27 +0900</pubDate>
    </item>
    <item>
      <title>두 개의 변량을 반영한 값을 만든다면 (#9251)</title>
      <link>https://theconcentrationoftime.tistory.com/164</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#9251 LCS &lt;a href=&quot;https://www.acmicpc.net/problem/9251&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9251&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아.. 그제 풀었던거 오늘 포스팅 하려니까 기억이 가물가물...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 dfs로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 경우의 수를 탐색하는 것이다. (브루트포스?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 역시나 메모리 초과를 맞이했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;틀린 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728814895653&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s1 = input().strip()
s2 = input().strip()

n1 = len(s1)
n2 = len(s2)

visited1 = [False] * n1
visited2 = [False] * n2

N = max(n1, n2)

result1 = {x: [] for x in range(1, n1 + 1)}
result2 = {x: [] for x in range(1, n1 + 1)}


def dfs(depth, s, origin, n):
    if depth &amp;gt;= len(origin):
        return

    if depth &amp;lt; len(origin):
        s += origin[depth]
        if n == 1:
            result1[len(s)].append(s)
        else:  # n==2
            result2[len(s)].append(s)

    dfs(depth + 1, s, origin, n)
    dfs(depth + 1, s[0:-1], origin, n)


dfs(0, &quot;&quot;, s1, 1)
dfs(0, &quot;&quot;, s2, 2)
# print(result1)
# print(result2)

for i in range(min(n1, n2), 0, -1):
    tmp1 = result1[i]
    tmp2 = result2[i]
    for x in tmp1:
        if x in tmp2:
            print(i)
            exit()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 하단의 알고리즘 분류를 보니 DP라고 나와있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하하.. 이걸 어떻게... 하고 찾아보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 DP였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 자꾸 2차원 DP 문제를 마주하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 한계를 돌파하기 위해서는 꼭 숙달해야 할 부분이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 사고방식을 통해 점화식을 찾을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lusfv/btsJ33d0HH9/siwbLA9EhbVwGfX1b6VQ01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lusfv/btsJ33d0HH9/siwbLA9EhbVwGfX1b6VQ01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lusfv/btsJ33d0HH9/siwbLA9EhbVwGfX1b6VQ01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flusfv%2FbtsJ33d0HH9%2FsiwbLA9EhbVwGfX1b6VQ01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;599&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP에서 점화식을 찾을 때, 수학적 점화식처럼 깔끔한 식이 나올 것이라 기대하지 말자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 테스트케이스를 손으로 최대한 굴려서, 규칙을 찾아내자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 정말 정말 중요한 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728815022780&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s1 = input().strip()
s2 = input().strip()

n1, n2 = len(s1), len(s2)
dp = [[0 for _ in range(n1 + 1)] for __ in range(n2 + 1)]

for y in range(1, n2 + 1):  # s1
    for x in range(1, n1 + 1):  # s2
        if s1[x - 1] == s2[y - 1]:
            dp[y][x] = dp[y - 1][x - 1] + 1
        else:
            dp[y][x] = max(dp[y - 1][x], dp[y][x - 1])

print(dp[n2][n1])&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>9251</category>
      <category>DP</category>
      <category>Python</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/164</guid>
      <comments>https://theconcentrationoftime.tistory.com/164#entry164comment</comments>
      <pubDate>Sun, 13 Oct 2024 19:24:05 +0900</pubDate>
    </item>
    <item>
      <title>트리의 지름을 구하는게 공식이 있다니... (#1167)</title>
      <link>https://theconcentrationoftime.tistory.com/163</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#1167 트리의 지름 &lt;a href=&quot;https://www.acmicpc.net/problem/1167&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1167&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 풀이하는 과정이 너무 복잡했다. (결국 블로그 참고함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 세 가지를 배울 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. DFS 구현의 두 가지 방식(재귀 vs 스택)의 장단점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하면, 스택으로 구현해야 메모리를 덜 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀로 짰더니 계속 메모리 초과가 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 비트마스크로 visited를 처리하는 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 리스트나 셋을 사용해서 노드 방문 여부를 체크하는 것과 달리 비트마스크로 구현하면 메모리 사용량을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 생각하면 기존에 [ 0, 1, 0, 1, 0, ... ] 이렇게 저장하던 visited 0/ 1을 그냥 붙여서 이진수로 표현하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ob01010...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;되게 심플하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 원하는 인덱스 만큼 1을 밀어놓은 이진수를 OR 연산을 통해 visited값에 반영해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진수를 몇 번 사용해본 사람이라면 금방 이해할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wizdom.tistory.com/258&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wizdom.tistory.com/258&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원소 추가는 or를 통해서 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원소 조회는 and를 통해서 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 심플하지만 효율적인 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 트리의 지름을 구하는 공식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는, dfs를 1부터 시작해서 제일 큰 누적 cost를 값으로 출력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그랬더니 틀렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보면, 트리의 지름에 1번 노드가 포함되지 않을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알아본 바로는,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 1번 노드에서 제일 멀리 있는 노드를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 그 노드에서 제일 멀리 있는 노드까지의 cost를 구한다. -&amp;gt; 정답&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 공식이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blogshine.tistory.com/111&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blogshine.tistory.com/111&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 이해하면 좋을까.. 감은 잡히는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728568793394&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque
import sys

input = sys.stdin.readline

n = int(input())

graph = {i : {} for i in range(1, n+1)}
# 리스트를 index로 관리해버리면, 인접 노드가 없는 노드에 대한 공간도 할당하기 때문에 메모리 초과가 난다.

for i in range(1, n+1):
    nums = list(map(int, input().split()))
    n = nums[0]
    nums = nums[1:-1]

    if len(nums) == 0:
        continue

    for j in range(0, len(nums), 2):
        graph[n][nums[j]] = nums[j+1]

# visited = [False for _ in range(n+1)]
# visited를 이진수로 처리한다고?? (비트마스크)
def dfs(start): # -&amp;gt; 재귀가 아니라 스택으로 바꿔야하나...
    visited = 0
    deep_dest = (None, -1) #node, cost
    stack = deque([(start, 0)])

    while stack:
        node, cost = stack.pop()
        visited |= (1&amp;lt;&amp;lt;node)
        if cost &amp;gt; deep_dest[1]:
            deep_dest = (node, cost)

        children = graph[node].keys()
        
        for v in children:
            if not (visited &amp;amp; (1 &amp;lt;&amp;lt; v)):
                stack.append((v, cost + graph[node][v]))
                # visited &amp;amp;= ~(1&amp;lt;&amp;lt;v) 트리는 돌아갈 필요가 없다고..?
    return deep_dest

target = dfs(1)[0]
distance = dfs(target)[1]
print(distance)

# 배운점
## 1. DFS구현 시 재귀와 스택을 사용할 떄 발생하는 메모리 사용량 차이
## 2. 비트마스크로 visited 체크 하는 방법 (메모리 사용량 감소)
## 3. 트리의 지름을 구하는 공식 (증명 이해 필요)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>1167</category>
      <category>dfs</category>
      <category>트리</category>
      <category>트리의 지름</category>
      <category>파이썬</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/163</guid>
      <comments>https://theconcentrationoftime.tistory.com/163#entry163comment</comments>
      <pubDate>Thu, 10 Oct 2024 23:00:05 +0900</pubDate>
    </item>
    <item>
      <title>dp를 2차원으로 확장시켜야... (#12865)</title>
      <link>https://theconcentrationoftime.tistory.com/162</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#12865 평범한 배낭 &lt;a href=&quot;https://www.acmicpc.net/problem/12865&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/12865&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 풀다가 해결을 못해서 버렸던 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;틀린코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728445156668&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n, k = map(int, input().split())

dp = [0] * (k + 1)

obj = []

for _ in range(n):
    w, v = map(int, input().split())
    dp[w] = v
    obj.append([w, v])

for i in range(k + 1):
    for w, v in obj:
        if i + w &amp;lt;= k:
            dp[i + w] = max(dp[i + w], dp[i] + v)

print(max(dp))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 자꾸 틀릴까? 의문이었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 잘 생각해보니, 물품의 중복이 가능한 코드였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 테스트케이스에서 극단적인 상황을 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무게 1, 가치 100짜리 물건이 있다면 어떻게 될까&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ppuKo/btsJ0bCtBXO/saInzT78rpwjmlihby8FJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ppuKo/btsJ0bCtBXO/saInzT78rpwjmlihby8FJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ppuKo/btsJ0bCtBXO/saInzT78rpwjmlihby8FJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FppuKo%2FbtsJ0bCtBXO%2FsaInzT78rpwjmlihby8FJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;288&quot; height=&quot;145&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게, 그 물건을 7개 담아버리면 가장 높은 가치가 된다. 이는 잘못된 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물건이 중복되면 안된다. 따라서 담은 물건을 기록하는 리스트를 추가해 dp를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답코드&lt;/p&gt;
&lt;pre id=&quot;code_1728445288853&quot; class=&quot;inform7&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import copy

n, k = map(int, input().split())

dp = [[0, []] for __ in range(k + 1)]
# [[value, [담은 물품의 flag]],  ......]
obj = []

for i in range(n):
    w, v = map(int, input().split())
    # dp[w][0] = v
    # dp[w][1][i] = True
    obj.append([w, v])

for prev_weight in range(k + 1):
    for i in range(n):
        w, v = obj[i]
        if prev_weight + w &amp;lt;= k:
            visited = dp[prev_weight][1]
            if i not in visited:
                if dp[prev_weight][0] + v &amp;gt; dp[prev_weight + w][0]:
                    value = dp[prev_weight][0] + v
                    dp[prev_weight + w] = [value, visited + [i]]

dp.sort(key=lambda x: x[0])

print(dp[-1][0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 뭔가 찝찝해서 다른 사람들의 정답을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@js43o/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@js43o/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728445318515&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[백준] 12865번: 평범한 배낭&quot; data-og-description=&quot;각각의 가치와 무게를 지닌 아이템들을 정해진 중량 내에서 최대의 가치가 되도록 선택하는 냅색 알고리즘 문제이다.가장 먼저 떠올린 것은 백트래킹을 이용하여 모든 아이템의 모든 조합의 수&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@js43o/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD&quot; data-og-url=&quot;https://velog.io/@js43o/백준-12865번-평범한-배낭&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGsTRE/hyXec3fBsF/hRzrk9jf7hEwnPAUDHcSw0/img.png?width=1165&amp;amp;height=465&amp;amp;face=0_0_1165_465,https://scrap.kakaocdn.net/dn/VImxd/hyXeb4kiw4/iVNySRwQAjDkNBMHoGBtT1/img.png?width=1165&amp;amp;height=465&amp;amp;face=0_0_1165_465,https://scrap.kakaocdn.net/dn/cMo9Hj/hyXd3SKkN7/ek4QPkbh9laxDMQLIzIbmK/img.png?width=1280&amp;amp;height=1280&amp;amp;face=0_0_1280_1280&quot;&gt;&lt;a href=&quot;https://velog.io/@js43o/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@js43o/%EB%B0%B1%EC%A4%80-12865%EB%B2%88-%ED%8F%89%EB%B2%94%ED%95%9C-%EB%B0%B0%EB%82%AD&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGsTRE/hyXec3fBsF/hRzrk9jf7hEwnPAUDHcSw0/img.png?width=1165&amp;amp;height=465&amp;amp;face=0_0_1165_465,https://scrap.kakaocdn.net/dn/VImxd/hyXeb4kiw4/iVNySRwQAjDkNBMHoGBtT1/img.png?width=1165&amp;amp;height=465&amp;amp;face=0_0_1165_465,https://scrap.kakaocdn.net/dn/cMo9Hj/hyXd3SKkN7/ek4QPkbh9laxDMQLIzIbmK/img.png?width=1280&amp;amp;height=1280&amp;amp;face=0_0_1280_1280');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[백준] 12865번: 평범한 배낭&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;각각의 가치와 무게를 지닌 아이템들을 정해진 중량 내에서 최대의 가치가 되도록 선택하는 냅색 알고리즘 문제이다.가장 먼저 떠올린 것은 백트래킹을 이용하여 모든 아이템의 모든 조합의 수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 리스트로 만드는게 정석이었던 것 같다 ㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물건의 중복이 안되는 상황이라면, 무게 / 아이템 형태의 2차원 리스트가 더 올바른 접근법일 것 같다. (시간복잡도 해결!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 문제가 안풀릴 때에는 극단적인 테스트케이스를 생각해볼 필요가 있는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 케이스를 만들기 보다는 기존의 테스트케이스에 새로운 값을 추가하는 방법도 좋은 접근인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐랄까,,, 일종의 &lt;b&gt;&quot;테스트 케이스 파생법&quot;&lt;/b&gt;?.. 뭐 이런 생각이 들었다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>12865</category>
      <category>DP</category>
      <category>Python</category>
      <category>파이썬</category>
      <category>평범한 배낭</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/162</guid>
      <comments>https://theconcentrationoftime.tistory.com/162#entry162comment</comments>
      <pubDate>Wed, 9 Oct 2024 12:42:51 +0900</pubDate>
    </item>
    <item>
      <title>그래프는 맨날까먹어~ (#1197)</title>
      <link>https://theconcentrationoftime.tistory.com/161</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;#1197 최소 스패닝 트리 (&lt;a href=&quot;https://www.acmicpc.net/problem/1197&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1197&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 문제풀이는 여러가지 개념이 필요한데... 하나씩 살펴보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래프를 어떻게 저장할 것인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 저장하는 방법은 세 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인접행렬 &amp;gt; 인접리스트&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인접행렬이 구현이 쉽다. 대신 그만큼 메모리와 시간을 많이 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인접리스트는 비교적 메모리와 시간을 적게 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 가장 효율적인 방법은 우선순위 큐라고 할 수 있다. 다만 이는 저장 방법이 아니라, 탐색 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 큐는 큐에 우선순위를 부여한 방식인데, 파이썬에서 이를 어떻게 구현할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heapq라이브러리를 쓴다. 이는 최소힙을 구현한 자료구조로, 0번 인덱스에 있는 값을 기준으로 정렬된다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 최소비용을 계산하기 위해 그래프를 저장한다면, (cost, node) 형태로 넣으면 된다. pop을 했을 때 cost가 작은 edge부터 return 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 인접 행렬 (Adjacency Matrix)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;설명&lt;/b&gt;: 정점 간의 연결 관계를 2차원 배열로 저장합니다. 행과 열의 교차점에 가중치를 넣습니다. 연결되지 않은 경우에는 보통 &lt;span&gt;0&lt;/span&gt;이나 &lt;span&gt;&amp;infin;&lt;/span&gt;로 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;예시&lt;/b&gt;: 4개의 노드를 가진 가중치 그래프&lt;/p&gt;
&lt;pre id=&quot;code_1728358211987&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 인접 행렬로 그래프 표현
graph_matrix = [
    [0, 1, 4, 0],  # 노드 0: 1과 4는 가중치, 0은 연결되지 않음
    [1, 0, 2, 5],  # 노드 1: 1과 2와 5로 연결
    [4, 2, 0, 1],  # 노드 2: 4와 2와 1로 연결
    [0, 5, 1, 0]   # 노드 3: 5와 1로 연결
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 인접 리스트 (Adjacency List)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;설명&lt;/b&gt;: 각 노드에 연결된 노드와 가중치를 리스트로 저장합니다. 인접 리스트는 딕셔너리나 리스트의 리스트로 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;예시&lt;/b&gt;: 4개의 노드를 가진 가중치 그래프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728358227825&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 인접 리스트로 그래프 표현
graph_list = {
    0: [(1, 1), (2, 4)],  # 노드 0: 노드 1과 가중치 1, 노드 2와 가중치 4
    1: [(0, 1), (2, 2), (3, 5)],  # 노드 1: 노드 0과 가중치 1, 노드 2와 가중치 2, 노드 3과 가중치 5
    2: [(0, 4), (1, 2), (3, 1)],  # 노드 2: 노드 0과 가중치 4, 노드 1과 가중치 2, 노드 3과 가중치 1
    3: [(1, 5), (2, 1)]  # 노드 3: 노드 1과 가중치 5, 노드 2와 가중치 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. [탐색방법] 우선순위 큐 (Priority Queue)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;설명&lt;/b&gt;: 우선순위 큐 자체는 그래프를 저장하는 방식이 아니라, 다익스트라 알고리즘에서 최단 거리 계산 시 사용할 큐입니다. 다익스트라 알고리즘에서 사용할 때는 보통 튜플 &lt;span&gt;(거리, 노드)&lt;/span&gt;를 형태로 힙큐에 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;b&gt;예시&lt;/b&gt;: &lt;span&gt;heapq&lt;/span&gt; 모듈을 사용한 초기화 예시&lt;/p&gt;
&lt;pre id=&quot;code_1728358246874&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq

# 우선순위 큐 초기화
priority_queue = []
heapq.heappush(priority_queue, (0, 0))  # (거리, 노드) 형태, 예를 들어 시작 노드 0의 거리는 0&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방향이 있는가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프가 단방향인지, 무방향인지 잘 판단해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 언급이 없다면, 보통 무방향 그래프이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무방향 그래프라면, 양 방향으로 그래프를 탐색할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 start-end, end-start를 모두 저장해줘야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddCF5z/btsJXTvBq6f/HVce3Pz7kLYB3SwAyo49Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddCF5z/btsJXTvBq6f/HVce3Pz7kLYB3SwAyo49Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddCF5z/btsJXTvBq6f/HVce3Pz7kLYB3SwAyo49Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddCF5z%2FbtsJXTvBq6f%2FHVce3Pz7kLYB3SwAyo49Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;212&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향이 있는 그래프라면 당연히 다음과 같이 해주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eieFAe/btsJX5bwaaZ/4cYJO0WNhoASCYHKHx0qi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eieFAe/btsJX5bwaaZ/4cYJO0WNhoASCYHKHx0qi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eieFAe/btsJX5bwaaZ/4cYJO0WNhoASCYHKHx0qi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeieFAe%2FbtsJX5bwaaZ%2F4cYJO0WNhoASCYHKHx0qi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;158&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;최소신장트리를 어떻게 구현할 것인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 프림 알고리즘과 크루스칼 알고리즘이 있다. 상황에 따라 적절한걸 사용해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[FROM GPT]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;프림&amp;nbsp;알고리즘&amp;nbsp;(Prim&amp;rsquo;s&amp;nbsp;Algorithm)&lt;br /&gt;&lt;br /&gt;&amp;bull; 작동&amp;nbsp;방식:&amp;nbsp;시작&amp;nbsp;정점에서&amp;nbsp;시작하여&amp;nbsp;연결된&amp;nbsp;간선&amp;nbsp;중&amp;nbsp;최소&amp;nbsp;가중치&amp;nbsp;간선을&amp;nbsp;선택해&amp;nbsp;트리에&amp;nbsp;추가합니다.&amp;nbsp;트리에서&amp;nbsp;연결되지&amp;nbsp;않은&amp;nbsp;정점과&amp;nbsp;연결되는&amp;nbsp;간선들&amp;nbsp;중&amp;nbsp;최소&amp;nbsp;가중치&amp;nbsp;간선을&amp;nbsp;계속&amp;nbsp;선택하여&amp;nbsp;트리를&amp;nbsp;확장해&amp;nbsp;나갑니다.&lt;br /&gt;&amp;bull; 시간복잡도:&lt;br /&gt;&amp;bull; 우선순위&amp;nbsp;큐를&amp;nbsp;사용하여&amp;nbsp;구현할&amp;nbsp;때:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;V)&lt;br /&gt;&amp;bull; 인접&amp;nbsp;행렬을&amp;nbsp;사용하여&amp;nbsp;구현할&amp;nbsp;때:&amp;nbsp;O(V^2)&lt;br /&gt;&amp;bull; 인접&amp;nbsp;리스트를&amp;nbsp;사용하고,&amp;nbsp;힙&amp;nbsp;자료구조를&amp;nbsp;사용하는&amp;nbsp;경우&amp;nbsp;O((V&amp;nbsp;+&amp;nbsp;E)&amp;nbsp;\log&amp;nbsp;V)&lt;br /&gt;&amp;bull; 적용&amp;nbsp;환경:&amp;nbsp;그래프가&amp;nbsp;밀집(dense)해&amp;nbsp;있는&amp;nbsp;경우(즉,&amp;nbsp;간선의&amp;nbsp;수&amp;nbsp;E가&amp;nbsp;많을&amp;nbsp;때)&amp;nbsp;효율적으로&amp;nbsp;동작합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728358361118&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq

def prim(graph, start):
    mst_cost = 0  # MST의 총 비용
    visited = set()  # 방문한 정점을 추적
    min_heap = [(0, start)]  # (가중치, 정점) 형태로 초기화
    
    while min_heap:
        cost, u = heapq.heappop(min_heap)
        
        if u in visited:
            continue
        
        # 현재 정점을 MST에 추가
        mst_cost += cost
        visited.add(u)
        
        # 인접한 모든 정점을 탐색
        for v, weight in graph[u]:
            if v not in visited:
                heapq.heappush(min_heap, (weight, v))
    
    return mst_cost

# 예시 그래프 (인접 리스트)
graph = {
    0: [(1, 4), (2, 3)],
    1: [(0, 4), (2, 1), (3, 2)],
    2: [(0, 3), (1, 1), (3, 4), (4, 3)],
    3: [(1, 2), (2, 4), (4, 2)],
    4: [(2, 3), (3, 2)]
}

# 시작 정점을 0으로 설정
mst_cost = prim(graph, 0)
print(&quot;Minimum Spanning Tree Cost (Prim):&quot;, mst_cost)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;크루스칼&amp;nbsp;알고리즘&amp;nbsp;(Kruskal&amp;rsquo;s&amp;nbsp;Algorithm)&lt;br /&gt;&lt;br /&gt;&amp;bull; 작동&amp;nbsp;방식:&amp;nbsp;모든&amp;nbsp;간선을&amp;nbsp;가중치&amp;nbsp;기준으로&amp;nbsp;오름차순&amp;nbsp;정렬한&amp;nbsp;후,&amp;nbsp;가장&amp;nbsp;작은&amp;nbsp;간선부터&amp;nbsp;선택합니다.&amp;nbsp;사이클이&amp;nbsp;생기지&amp;nbsp;않도록&amp;nbsp;확인하면서&amp;nbsp;트리에&amp;nbsp;추가하고,&amp;nbsp;트리에&amp;nbsp;포함된&amp;nbsp;간선의&amp;nbsp;개수가&amp;nbsp;V-1개가&amp;nbsp;될&amp;nbsp;때까지&amp;nbsp;반복합니다.&lt;br /&gt;&amp;bull; 시간복잡도:&lt;br /&gt;&amp;bull; 간선을&amp;nbsp;정렬하는&amp;nbsp;시간:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;E)&lt;br /&gt;&amp;bull; 유니온-파인드(Union-Find)를&amp;nbsp;통한&amp;nbsp;사이클&amp;nbsp;검사:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;V)&lt;br /&gt;&amp;bull; 전체&amp;nbsp;시간복잡도:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;E)&amp;nbsp;또는&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;V)&amp;nbsp;(간선&amp;nbsp;정렬이&amp;nbsp;주&amp;nbsp;시간복잡도를&amp;nbsp;차지)&lt;br /&gt;&amp;bull; 적용&amp;nbsp;환경:&amp;nbsp;그래프가&amp;nbsp;희소(sparse)해&amp;nbsp;있는&amp;nbsp;경우(즉,&amp;nbsp;간선의&amp;nbsp;수&amp;nbsp;E가&amp;nbsp;적을&amp;nbsp;때)&amp;nbsp;효율적으로&amp;nbsp;동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728358379729&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        
        if root_u != root_v:
            if self.rank[root_u] &amp;gt; self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] &amp;lt; self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(n, edges):
    # 간선들을 가중치 기준으로 정렬
    edges.sort(key=lambda x: x[2])
    uf = UnionFind(n)
    mst_cost = 0
    mst_edges = []
    
    for u, v, weight in edges:
        # 간선이 사이클을 형성하지 않으면 MST에 추가
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            mst_cost += weight
            mst_edges.append((u, v, weight))
    
    return mst_cost, mst_edges

# 예시 그래프 (간선 리스트)
# 각 간선은 (정점1, 정점2, 가중치) 형태로 나타냄
edges = [
    (0, 1, 4),
    (0, 2, 3),
    (1, 2, 1),
    (1, 3, 2),
    (2, 3, 4),
    (2, 4, 3),
    (3, 4, 2)
]

# 정점 개수와 간선 리스트 전달
mst_cost, mst_edges = kruskal(5, edges)
print(&quot;Minimum Spanning Tree Cost (Kruskal):&quot;, mst_cost)
print(&quot;Edges in MST:&quot;, mst_edges)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;시간복잡도&amp;nbsp;비교&lt;br /&gt;&lt;br /&gt;&amp;bull; 그래프의&amp;nbsp;밀도에&amp;nbsp;따라&amp;nbsp;두&amp;nbsp;알고리즘&amp;nbsp;중&amp;nbsp;선택이&amp;nbsp;달라집니다.&lt;br /&gt;&amp;bull; 밀집&amp;nbsp;그래프:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;V)인&amp;nbsp;프림&amp;nbsp;알고리즘이&amp;nbsp;더&amp;nbsp;유리합니다.&amp;nbsp;간선의&amp;nbsp;수&amp;nbsp;E가&amp;nbsp;V^2에&amp;nbsp;가까워지기&amp;nbsp;때문에,&amp;nbsp;정렬&amp;nbsp;없이&amp;nbsp;바로&amp;nbsp;우선순위&amp;nbsp;큐를&amp;nbsp;이용해&amp;nbsp;빠르게&amp;nbsp;간선을&amp;nbsp;선택하는&amp;nbsp;것이&amp;nbsp;효과적입니다.&lt;br /&gt;&amp;bull; 희소&amp;nbsp;그래프:&amp;nbsp;O(E&amp;nbsp;\log&amp;nbsp;E)인&amp;nbsp;크루스칼&amp;nbsp;알고리즘이&amp;nbsp;더&amp;nbsp;적합합니다.&amp;nbsp;간선의&amp;nbsp;수가&amp;nbsp;적어&amp;nbsp;정렬의&amp;nbsp;부담이&amp;nbsp;크지&amp;nbsp;않으며,&amp;nbsp;각&amp;nbsp;간선을&amp;nbsp;하나씩&amp;nbsp;탐색하여&amp;nbsp;트리를&amp;nbsp;구축하는&amp;nbsp;방식이&amp;nbsp;효율적입니다.&lt;br /&gt;&lt;br /&gt;따라서,&amp;nbsp;그래프의&amp;nbsp;밀집도와&amp;nbsp;구현&amp;nbsp;방식에&amp;nbsp;따라&amp;nbsp;프림과&amp;nbsp;크루스칼&amp;nbsp;알고리즘의&amp;nbsp;효율성&amp;nbsp;차이가&amp;nbsp;발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그럼 다익스트라는 뭐냐고?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라는 특정 노드에서 특정 노드까지의 최단거리를 계산하는 그리디 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*(그리디 알고리즘 = 최적값만 찾는 알고리즘, 브루트포스와 상응하는 개념)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출발지점과 도착지점이 있다는 점에서, 프림/크루스칼과 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다. 그럼 이러한 개념들을 모두 고려한, 이 문제에 대한 답은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728358414702&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys, heapq

input = sys.stdin.readline

v, e = map(int, input().split())

graph = [[] for _ in range(v + 1)]

for _ in range(e):
    start, end, edge = map(int, input().split())
    # 무향그래프
    graph[start].append([edge, end])
    graph[end].append([edge, start])

answer = 0
visited = [False for _ in range(v + 1)]
min_heap = [(0, 1)]
# visited[1] = True

while min_heap:
    edge, node = heapq.heappop(min_heap)

    # 방문한 곳 처리
    if visited[node]:
        continue

    # 방문처리
    answer += edge
    visited[node] = True

    # 주변 엣지 탐색
    for edge, next in graph[node]:
        if not visited[next]:
            heapq.heappush(min_heap, (edge, next))

print(answer)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 틀려서 당황했는데, 무향그래프인점을 반영하지 않아서였다.&lt;/p&gt;</description>
      <category>Algorithm/acmicpc.net</category>
      <category>1197</category>
      <category>그래프</category>
      <category>탐색</category>
      <category>파이썬</category>
      <category>프림</category>
      <author>winney916</author>
      <guid isPermaLink="true">https://theconcentrationoftime.tistory.com/161</guid>
      <comments>https://theconcentrationoftime.tistory.com/161#entry161comment</comments>
      <pubDate>Tue, 8 Oct 2024 12:34:04 +0900</pubDate>
    </item>
  </channel>
</rss>