<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>min.log</title>
        <link>https://velog.io/</link>
        <description>몸이 셋 쯤 되고 싶은 초보 개발자</description>
        <lastBuildDate>Sun, 29 Jun 2025 17:24:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>min.log</title>
            <url>https://velog.velcdn.com/images/not_mean_min/profile/81e6e244-ff38-4aed-8ab3-8ede0ae53847/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. min.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/not_mean_min" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백준 27923 햄버거최대 몇개드실수있나요?]]></title>
            <link>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-27923-%ED%96%84%EB%B2%84%EA%B1%B0%EC%B5%9C%EB%8C%80-%EB%AA%87%EA%B0%9C%EB%93%9C%EC%8B%A4%EC%88%98%EC%9E%88%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-27923-%ED%96%84%EB%B2%84%EA%B1%B0%EC%B5%9C%EB%8C%80-%EB%AA%87%EA%B0%9C%EB%93%9C%EC%8B%A4%EC%88%98%EC%9E%88%EB%82%98%EC%9A%94</guid>
            <pubDate>Sun, 29 Jun 2025 17:24:22 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-정보">문제 정보</h2>
<p><a href="https://www.acmicpc.net/problem/27923">문제링크</a>
햄버거의 질량을 줄여주는 콜라의 효과 지속 시간과 배치도가 제공되었을 때, 햄버거의 순서를 바꿔서 햄버거의 질량을 최소한으로 만들었을 때의 값을 구하는 문제이다.</p>
<h2 id="배경-지식">배경 지식</h2>
<p>콜라의 지속시간이 있으므로 콜라 하나 당 여러 개의 햄버거에 영향을 끼칠 수 있는 구조이다. 지속시간 동안 for문을 돌면서 업데이트를 시킬 수도 있겠지만, 그렇게 될 경우 최악의 경우에 (콜라의 개수가 20만이고, 지속시간이 20만이며, 모든 콜라를 첫 번째에 마실 경우) 한 개의 콜라에 20만씩, 콜라의 영향을 계산하는 데에만 4000억번의 연산을 해야할 수도 있다. 따라서 이런 영역 계산을 줄여주기 위해서 사용하는 알고리즘이 차분배열 또는 imos 알고리즘이다.</p>
<h3 id="imos-indexed-modification-of-sums법">imos (Indexed Modification of Sums)법</h3>
<ul>
<li>특정 구간을 빠르게 업데이트하고, 이를 통해 누적합을 구할 때 유용한 알고리즘 </li>
<li>핵심은 업데이트 구간을 배열에 기록 후, 누적 합을 통해 최종 값을 계산 </li>
</ul>
<p>즉 아까 말한 최악의 케이스의 경우 0번 인덱스에 콜라가 사용되었고, 20만1번째에 콜라 사용량을 -1을 한다고만 기록하는 것이다. 20만의 연산이 2번으로 줄었다. 이렇게 양이 늘어날 경우와 줄어드는 타이밍만 기록해서 나중에 한번에 몰아서 업데이트 하는 방식이다. 차분배열이라고도 한다. imos는 1차원 배열 뿐만 아니라 n차원 배열에서도 사용 가능한데, 다른 문제를 풀면서 다시 한 번 연습해볼 예정.</p>
<h2 id="구현">구현</h2>
<pre><code class="language-python"># 햄버거의 개수, 햄버거의 질량, 콜라의 지속시간
n, k, l = map(int, input().split())
# 햄버거의 질량 리스트. 나중의 계산을 위해 미리 정렬한다.
h = sorted(map(int, input().split()))

# 효과를 기록해둘 차분배열 e
e = [0]*n
for i in map(int, input().split()):
    e[i-1]+=1
    if i+l &lt;= n:
        e[i+l-1] -= 1

for i in range(1,n):
    e[i] += e[i-1]

# 콜라 효과가 가장 적을 때에 가장 작은 햄버거를 먹을 수 있도록 정렬해서 확인한다.
e.sort()

# e[j]가 30 이상일 경우 분모가 분자의 최대값보다 더 커지므로 신경쓰지 않아도 된다.
# 2^30 = 1,073,741,824 &gt; 10^9
result = sum(h[j] &gt;&gt; e[j] for j in range(n) if e[j] &lt;30)
print(result)</code></pre>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/not_mean_min/post/d4a816e8-8bd4-41c0-b20d-9dffebdc4c54/image.png" alt="">
와! 62명 중이긴 하지만 1등!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1197 최소 스패닝 트리준 1197 최소 스패닝 트리]]></title>
            <link>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Sun, 22 Jun 2025 17:36:40 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-정보">문제 정보</h2>
<p><a href="https://www.acmicpc.net/problem/1197">문제링크</a>
최소 스패닝 트리(최소 신장 트리)를 구했을 때의 가중치를 구하는 문제이다.</p>
<h2 id="배경-지식">배경 지식</h2>
<p>스패닝 트리는 다음의 조건을 만족하는 트리를 의미한다.</p>
<p><code>(1) 모든 정점을 포함하고,</code>
<code>(2) 정점 간 서로 연결이 되는 트리</code></p>
<p>그리고 트리의 조건을 분리하면
<code>(3) 싸이클이 존재하지 않는 그래프</code></p>
<p>까지 세 가지 조건을 의미한다.</p>
<p>이 때 간선들의 가중치가 제시되어 있고, 해당 간선들의 비용 중 최소의 비용을 만족하는 최소 신장 트리를 구하는 대표적인 알고리즘은 크루스칼 알고리즘과 프림 알고리즘이다.</p>
<h3 id="크루스칼-알고리즘과-프림-알고리즘">크루스칼 알고리즘과 프림 알고리즘</h3>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>핵심 아이디어</th>
<th>시간복잡도</th>
<th>특이사항</th>
</tr>
</thead>
<tbody><tr>
<td>크루스칼</td>
<td>간선 정렬 후 사이클 없는 것만 선택</td>
<td>𝑂(𝐸 log𝐸)</td>
<td>Union-Find 사용, 희소 그래프에 유리</td>
</tr>
<tr>
<td>프림</td>
<td>현재 정점에서 인접한 최소 간선 선택</td>
<td>𝑂(𝐸 log𝑉)</td>
<td>우선순위 큐 사용, 조밀 그래프에 유리</td>
</tr>
</tbody></table>
<p><strong>정점 기준</strong>으로 가장 가중치가 작은 간선을 선택할 지, 
<strong>간선 기준</strong>으로 가장 가중치가 작은 간선 중에서 사이클이 없는 것을 선택할지에 따라서
두 알고리즘은 구분이 된다.
정점 대비 간선의 비중에 따라서 선택하면 되는데, 임의의 두 정점에 대해 최대 하나의 간선만 존재할 수 있다는 가정 하에 무방향 그래프의 최대 간선 수 대비 간선의 비율로 계산한다고 한다. GPT의 도움에 따르면 계산식은 다음과 같다.
<img src="https://velog.velcdn.com/images/not_mean_min/post/28176c2f-fba9-4f6f-a1ac-a84cfe231ed6/image.png" alt="그래프의 밀도"></p>
<p>이 밀도가 0.5 이상이면 조밀, 0.1 이하면 희소한 그래프로 본다고 한다.</p>
<p>문제의 제한사항에 따르면 정점의 최대 개수가 10,000개, 간선의 최대 개수가 100,000개이므로</p>
<p>해당 계산식에 따르면 최악의 경우 밀도는 0.002, 따라서 크루스칼 알고리즘이다. 
<img src="https://velog.velcdn.com/images/not_mean_min/post/224aeb5f-6d64-4e0d-9714-8ab8caa19e8c/image.png" alt=""></p>
<h2 id="구현">구현</h2>
<pre><code class="language-python">import sys
import heapq
# input의 특성 상 여러 줄을 읽어와야 할 때 sys.stdin.readline이 더 빠른데
# 해당 문제는 최악의 경우 100,000줄을 읽어와야 하므로 input을 대체
input = sys.stdin.readline
# Recursion Error가 나서 재귀의 한도를 늘렸다. Python의 default 재귀 한도는 10**3
sys.setrecursionlimit(10**5)

# Union Find 구현
def union(n1, n2):
    parents[n2] = n1

def find(n):
    if parents[n] == n:
        return n
    else:
        parents[n] = find(parents[n])
        return parents[n]

v, e = map(int,input().split())
parents = [i for i in range(v+1)]
# 최소 힙으로 쓸 q
q = []

# 사용된 간선의 개수를 세서 불필요한 계산을 막을 변수
# 간선의 개수는 정점의 개수보다 하나 적으므로 초기값으로 1 할당
linked = 1

ans = 0

for _ in range(e):
    s, e, w = map(int,input().split())
    heapq.heappush(q, (w, s, e))

while linked != v:
    w, s, e = heapq.heappop(q)
    s = find(s)
    e = find(e)
    if s != e:
        union(s, e)
        ans += w
        linked += 1

print(ans)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 17837 새로운 게임]]></title>
            <link>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-17837-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@not_mean_min/%EB%B0%B1%EC%A4%80-17837-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Sun, 05 Jan 2025 04:48:07 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/17837">문제 링크</a></p>
<p>연결되어 있는 피스들이 이동해야 하니까 연결리스트로 구현하면 좋겠다고 생각해서 그대로 만들어서 구현해봤다.
근데 이 애들을 확인하는 논리 과정에서 불필요한 과정이 있었는지, 아니면 접근 자체가 잘못 한건지(0.5초니까) 확인해보고, 문제 풀이를 다시 갱신할 예정.</p>
<pre><code class="language-python">class Piece:
    def __init__(self, direction):
        self.upper = None  # 이 말 위에 있는 말과 연결
        self.downer = None  # 이 말 아래에 있는 말과 연결
        self.delta = deltas[direction - 1]  # 이동 방향 설정
        self.reversed = 1  # 이동 방향 반전 여부
        self.y = 0  # 말의 현재 y 좌표
        self.x = 0  # 말의 현재 x 좌표

    def move(self, board, pieces):
        # 새로운 위치 계산
        new_y = self.y + self.delta[0] * self.reversed
        new_x = self.x + self.delta[1] * self.reversed

        # 새 위치가 체스판 범위를 벗어나거나 파란색(2)인 경우
        if not (0 &lt;= new_y &lt; len(board) and 0 &lt;= new_x &lt; len(board)) or board[new_y][new_x] == 2:
            self.reversed *= -1  # 이동 방향 반전
            new_y = self.y + self.delta[0] * self.reversed
            new_x = self.x + self.delta[1] * self.reversed
            # 다시 확인 후 여전히 파란색이거나 범위를 벗어나면 이동하지 않음
            if not (0 &lt;= new_y &lt; len(board) and 0 &lt;= new_x &lt; len(board)) or board[new_y][new_x] == 2:
                return False

        # 현재 위치에서 연결 해제
        if self.downer:
            self.downer.upper = None
            self.downer = None

        # 현재 말과 그 위에 쌓인 말들을 이동 스택에 추가
        current = self
        moving_stack = [current]
        while current.upper:
            moving_stack.append(current.upper)
            current = current.upper

        # 새 위치가 빨간색(1)인 경우 이동 스택을 뒤집음
        if board[new_y][new_x] == 1:
            moving_stack = moving_stack[::-1]

        # 새 위치에 이미 존재하는 맨 위 말을 찾음
        top_piece = None
        for p in pieces:
            if p.y == new_y and p.x == new_x:
                top_piece = p
                while top_piece.upper:
                    top_piece = top_piece.upper
                break

        # 새 위치의 기존 말과 이동 스택 연결
        if top_piece:
            moving_stack[0].downer = top_piece
            top_piece.upper = moving_stack[0]

        # 이동 스택의 모든 말의 위치 업데이트
        for p in moving_stack:
            p.y = new_y
            p.x = new_x

        # 이동 스택 내부 말들끼리 연결
        for i in range(len(moving_stack) - 1):
            moving_stack[i].upper = moving_stack[i + 1]
            moving_stack[i + 1].downer = moving_stack[i]

        # 새 위치에서의 스택 높이 계산
        stack_height = 1
        current = moving_stack[-1]
        while current.downer:
            stack_height += 1
            current = current.downer

        return stack_height &gt;= 4  # 스택 높이가 4 이상인지 여부 반환


# 게임 메인 로직
deltas = ((0, 1), (0, -1), (-1, 0), (1, 0))  # 방향: 오른쪽, 왼쪽, 위쪽, 아래쪽
n, k = map(int, input().split())  # 체스판 크기와 말 개수
board = [list(map(int, input().split())) for _ in range(n)]  # 체스판 상태
pieces = []

# 말 초기화
for i in range(k):
    y, x, direction = map(int, input().split())
    piece = Piece(direction)  # 말 생성
    piece.y = y - 1  # 0부터 시작하도록 변환
    piece.x = x - 1
    pieces.append(piece)

turn = 0  # 턴 수 초기화
game_over = False  # 게임 종료 여부
while turn &lt; 1000:  # 최대 1000턴까지 시도
    turn += 1
    for piece in pieces:
        if piece.downer is None:  # 가장 아래에 있는 말만 이동
            if piece.move(board, pieces):  # 이동 후 종료 조건 확인
                game_over = True
                break
    if game_over:
        break

print(turn if game_over else -1)  # 종료된 턴 출력, 아니면 -1 출력</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java - 제네릭 타입]]></title>
            <link>https://velog.io/@not_mean_min/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@not_mean_min/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85</guid>
            <pubDate>Fri, 20 Dec 2024 04:01:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java의 제네릭 타입은 클래스, 인터페이스, 메서드를 정의할 때 타입을 매개변수화할 수 있게 해주는 기능이다. 이를 통해 코드의 재사용성과 타입 안정성을 높일 수 있다.</p>
</blockquote>
<h3 id="장점">장점</h3>
<ol>
<li>타입 안정성: 컴파일 시점에 타입 체크를 수행하여 런타임 오류를 방지한다.</li>
<li>코드 재사용: 다양한 데이터 타입에 대해 동일한 코드를 사용할 수 있다.</li>
<li>타입 캐스팅 제거: 명시적인 타입 캐스팅이 필요 없어 코드가 간결해진다.</li>
</ol>
<h3 id="정의-방식">정의 방식</h3>
<h4 id="제네릭-클래스">제네릭 클래스</h4>
<pre><code class="language-java">public class Box&lt;T&gt; {
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}</code></pre>
<p>여기에서의 T는 타입 매개변수이다.</p>
<h4 id="제네릭-메서드">제네릭 메서드</h4>
<pre><code class="language-java">public &lt;T&gt; void printArray(T[] array) {
    for (T element : array) {
        System.out.print(element + &quot; &quot;);
    }
    System.out.println();
}</code></pre>
<h3 id="제네릭의-제한">제네릭의 제한</h3>
<p>상한 경계: <code>&lt;T extends SuperClass&gt;</code>로 정의하여 특정 클래스의 하위 타입만 허용할 수 있다.
하한 경계: <code>&lt;T super SubClass&gt;</code>로 정의하여 특정 클래스의 상위 타입만 허용할 수 있다.
와일드카드: ?를 사용하여 알 수 없는 타입을 표현할 수 있다.</p>
<h3 id="관례">관례</h3>
<p>타입에 주로 사용하는 키워드</p>
<pre><code>E - Element
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types</code></pre><h3 id="활용">활용</h3>
<h4 id="다양한-타입에-대한-테스트">다양한 타입에 대한 테스트</h4>
<pre><code class="language-java">@Test
public void testGenericMethod() {
    Integer[] intArray = {1, 5, 3, 7, 2};
    String[] strArray = {&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;};

    assertEquals(Integer.valueOf(7), findMax(intArray));
    assertEquals(&quot;cherry&quot;, findMax(strArray));
}

public static &lt;T extends Comparable&lt;T&gt;&gt; T findMax(T[] arr) {
    // findMax 메서드 구현
}</code></pre>
<h4 id="경계성-테스트에-대한-확장성-보장">경계성 테스트에 대한 확장성 보장</h4>
<pre><code class="language-java">public &lt;T extends Number&gt; double sumOfList(List&lt;T&gt; list) {
    return list.stream().mapToDouble(Number::doubleValue).sum();
}

@Test
public void testSumOfList() {
    List&lt;Integer&gt; intList = Arrays.asList(1, 2, 3);
    List&lt;Double&gt; doubleList = Arrays.asList(1.1, 2.2, 3.3);

    assertEquals(6.0, sumOfList(intList), 0.001);
    assertEquals(6.6, sumOfList(doubleList), 0.001);
}</code></pre>
<p>만약 제네릭을 사용하지 않고 코드를 작성하면, 아래와 같은 문제가 발생할 수 있다.</p>
<pre><code class="language-java">public double sumOfList(List&lt;Number&gt; list) {
    return list.stream().mapToDouble(Number::doubleValue).sum();
}</code></pre>
<p>위처럼 List&lt;Number&gt;로 타입을 고정시키면, List&lt;Integer&gt;나 List&lt;Double&gt; 같은 구체적인 타입은 전달할 수 없다. 왜냐하면 List&lt;Integer&gt;는 List&lt;Number&gt;의 서브타입이 아니기 때문이다.
(Java에서 제네릭은 공변성을 지원하지 않으므로 List&lt;Number&gt;는 List&lt;Integer&gt;와 호환되지 않는다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 양궁대회]]></title>
            <link>https://velog.io/@not_mean_min/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%96%91%EA%B6%81%EB%8C%80%ED%9A%8C</link>
            <guid>https://velog.io/@not_mean_min/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%96%91%EA%B6%81%EB%8C%80%ED%9A%8C</guid>
            <pubDate>Tue, 17 Dec 2024 12:55:30 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/92342">문제 링크</a></p>
<ol>
<li>상대방과의 점수차가 가장 큰 경우의 수를 찾되</li>
<li>같은 점수일 경우 가장 낮은 과녁에 쏜 경우를 return해야 하는 최적화 문제.</li>
</ol>
<p>쏠 수 있는 화살의 갯수인 n의 크기가 10이하, 화살을 쏠 수 있는 위치도 11가지로 제한되어 있어 수월해야 할 문제이다. 근데 생각보다 어려워서 쉽지 않았다.</p>
<p>포인트</p>
<ol>
<li>상대방이 이미 화살을 쏜 곳에 화살을 쏘는 경우 상대방의 점수가 감소 + 내 점수가 증가하므로 2배의 효용을 얻을 수 있다.</li>
<li>어디에 화살을 쏘더라도 이득이 없는 경우 화살은 0점에 몰아서 쏴야 한다.</li>
</ol>
<h3 id="1-기댓값">1. 기댓값</h3>
<pre><code class="language-python">def solution(n, info):
    answer = [0]*11
    # 정답을 담을 리스트
    weight = [0]*11
    # 각 과녁에 화살을 쏠 경우 한 화살 당 기댓값을 담을 리스트
    # 화살 한방 당 기댓값 + 점수 기록
    for i in range(11):
        if info[i]:
            weight[i] = ((10-i) * 2 / (info[i]+1), 10-i)
            # 상대방의 점수를 뺏어오는 것 + 내 점수에 더해지는 것
            # =&gt; 2배의 점수를 얻는 데 필요한 화살발수만큼으로 나눔
        else:
            weight[i] = ((10-i), 10-i)
            # 내 점수에 더해지는 것만 1발을 쏴서 얻을 수 있다.
    weight.sort(key = lambda x: (-x[0],-x[1]))
    # 화살 한 방당 기댓값이 높은 애들 먼저 + 높은 점수 먼저
    for dump, index in weight:
        if n &gt; info[10-index]:
            answer[10-index] = info[10-index] +1
            n -= info[10-index]+1
    answer[10] = n
    # 남는 화살은 0점 과녁에 몰기
    a_score = 0
    r_score = 0
    # 어피치와 라이언의 점수 계산하기
    for i in range(10):
        if answer[i] or info[i]:
            if answer[i] &gt; info[i]:
                r_score += (10-i)
            else:
                a_score += (10-i)
    if a_score &gt;= r_score:
        return [-1]
    else: return answer</code></pre>
<p>그런데 <code>solution(2, [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0])</code>의 경우 9점에다 2발을 쏴서 1점 차로 이기는 경우가 최적임에도 불구하고 한 발의 기댓값이 10점에 쏘는 경우가 제일 높고, 그 다음에 9, 8점에 쏘는 기댓값이 높으나 불가능하여 7점에 한 발을 쏘게 되어 동점으로 결과가 남는 경우가 등장했다. 어떤 방식으로 개선을 해야 할 지 모르겠어서 다른 방식을 채택했다.</p>
<h3 id="2-dfs">2. DFS</h3>
<pre><code class="language-python">def solution(n, info):
    # 최종 결과로 반환할 최대 점수 차이와 가장 좋은 화살 배분 결과를 저장
    max_diff = 0
    best_result = [-1]

    # 라이언과 어피치의 점수를 계산하는 함수
    def calculate_score(ryan):
        apeach_score, ryan_score = 0, 0
        for i in range(11):  # 0점 ~ 10점까지 반복
            # 어피치와 라이언 둘 다 해당 점수에 화살을 쏘지 않은 경우
            if info[i] == 0 and ryan[i] == 0:
                continue
            # 어피치가 같은 점수에서 화살을 더 많이 쏜 경우
            if info[i] &gt;= ryan[i]:
                apeach_score += 10 - i  # 어피치 점수에 추가
            else:
                ryan_score += 10 - i  # 라이언 점수에 추가
        # 라이언 점수가 어피치 점수보다 높을 때만 점수 차이를 반환
        return ryan_score - apeach_score if ryan_score &gt; apeach_score else 0

    # 백트래킹 함수: 화살을 배분하는 모든 경우의 수를 탐색
    def backtrack(idx, arrows_left, ryan):
        nonlocal max_diff, best_result  # 함수 외부 변수에 접근하기 위해 nonlocal 사용

        # 종료 조건: 모든 점수를 확인했거나 화살을 모두 사용했을 때
        if idx == 11 or arrows_left == 0:
            ryan[10] += arrows_left  # 남은 화살을 0점에 모두 배치
            diff = calculate_score(ryan)  # 점수 차이 계산
            # 점수 차이가 최대값이거나 같은 점수 차이일 때 더 낮은 점수를 많이 맞힌 경우 갱신
            if diff &gt; max_diff or (diff == max_diff and ryan[::-1] &gt; best_result[::-1]):
                max_diff = diff  # 최대 점수 차이 갱신
                best_result = ryan[:]  # 현재 화살 배분 상태 저장
            ryan[10] -= arrows_left  # 원래 상태로 복구
            return

        # 1. 라이언이 현재 점수에서 이기기 위해 화살을 쏘는 경우
        if arrows_left &gt; info[idx]:  # 남은 화살이 어피치보다 더 많이 쏠 수 있을 때
            ryan[idx] = info[idx] + 1  # 어피치보다 1발 더 쏨
            backtrack(idx + 1, arrows_left - ryan[idx], ryan)  # 다음 점수로 이동
            ryan[idx] = 0  # 원래 상태로 되돌림

        # 2. 현재 점수에 화살을 쏘지 않는 경우
        backtrack(idx + 1, arrows_left, ryan)

    # 백트래킹 함수 호출: 0번 점수부터 시작하고 n발의 화살을 배분
    backtrack(0, n, [0] * 11)

    # 결과 반환: 점수 차이가 0보다 크면 최적의 화살 배분, 그렇지 않으면 [-1]
    return best_result if max_diff &gt; 0 else [-1]</code></pre>
<p>그래서 모든 과녁에 화살을 쏘는 방식으로 완전탐색을 했다. 그랬더니 통과는 했지만... 만약에 과녁이 11개만 있는게 아니라 더 다양하게 있었다면? n이 늘어난다 하면 이대로 괜찮을까? 싶어서 다른 방식으로도 풀어봤다.</p>
<p><img src="https://velog.velcdn.com/images/not_mean_min/post/714b5a45-826a-434f-959a-92a6efbdb578/image.png" alt="DFS 방식 풀이 결과"></p>
<h3 id="3-dp">3. DP</h3>
<pre><code class="language-python">def solution(n, info):
    # 상대방(어피치)의 초기 점수 총합을 계산
    # info[i]가 1 이상이면 어피치가 해당 점수(10-i)를 획득한 것이므로 이를 더함
    counterpart_score = sum((10 - i) for i in range(10) if info[i])

    # DP 테이블 초기화: 
    # dp[j]는 j개의 화살을 사용했을 때의 [총 점수, 점수를 얻은 칸 위치 리스트]
    dp = [[0, []] for _ in range(n + 1)]

    # 각 점수 칸(10점부터 0점까지)을 탐색
    for i in range(10):
        c = info[i]  # 어피치가 해당 칸에 쏜 화살의 개수

        if c != 0:  
            # 상대방이 해당 점수 칸에서 이미 점수를 획득한 경우
            # 내가 점수를 얻으려면 상대방보다 1발 더 많이 쏴야 한다 (c + 1발 필요)
            for j in range(n - c - 1, -1, -1):  # 남은 화살 수를 거꾸로 탐색
                # j개의 화살을 사용한 상태에서 점수를 획득할 수 있는 조건
                # 조건 1: 이전 점수가 존재해야 함 (j == 0이거나 dp[j][0]이 존재)
                # 조건 2: 현재 점수를 더했을 때 최댓값을 갱신하는 경우
                if (j == 0 or dp[j][0]) and dp[j][0] + 2 * (10 - i) &gt;= dp[j + c + 1][0]:
                    # 화살을 쏴서 점수 갱신
                    dp[j + c + 1][0] = dp[j][0] + 2 * (10 - i)
                    # 점수를 얻은 위치 리스트를 업데이트 (복사 후 append)
                    dp[j + c + 1][1] = dp[j][1].copy()
                    dp[j + c + 1][1].append(10 - i)
        else:
            # 상대방이 해당 점수 칸에 화살을 하나도 쏘지 않은 경우
            # 내가 화살을 1발만 쏴도 점수를 획득할 수 있음
            for j in range(n - 1, -1, -1):  # 남은 화살 수를 거꾸로 탐색
                # 조건 1: 이전 점수가 존재해야 함
                # 조건 2: 현재 점수를 더했을 때 최댓값을 갱신하는 경우
                if (j == 0 or dp[j][0]) and dp[j][0] + (10 - i) &gt;= dp[j + 1][0]:
                    # 점수 갱신: 이전 점수에 (10 - i) 점수를 추가
                    dp[j + 1][0] = dp[j][0] + (10 - i)
                    # 점수를 얻은 위치 리스트를 업데이트
                    dp[j + 1][1] = dp[j][1].copy()
                    dp[j + 1][1].append(10 - i)

    # DP 테이블에서 최대 점수를 가지는 결과를 찾음
    max_comb = max(dp, key=lambda x: x[0])

    # 어피치의 점수를 넘지 못하면 라이언이 이길 수 없으므로 [-1] 반환
    if max_comb[0] &lt;= counterpart_score:
        return [-1]

    # 결과 배열: 0점부터 10점까지 각 점수 칸에 라이언이 쏜 화살 개수를 저장
    ans = [0] * 11

    # max_comb[1]에 기록된 점수를 바탕으로 화살 개수를 할당
    for num in max_comb[1]:
        ans[10 - num] = info[10 - num] + 1  # 어피치보다 1발 더 많이 쏨

    # 남은 화살이 있으면 0점 칸에 모두 할당
    ans[10] = max(0, n - sum(ans))

    return ans</code></pre>
<p>DP로 j개의 화살을 쐈을 때의 최댓값을 갱신해나가면서 최적해를 찾아낸다. 처음에 DP의 값을 앞에서부터 갱신했더니 최적해로 저장될 가능성이 있는 값들이 덮어 씌워지는 문제가 있어 뒤에서부터 앞으로 값을 갱신해나갔다. 
<img src="https://velog.velcdn.com/images/not_mean_min/post/83120f05-45b4-463e-bdcf-abf30b60c18b/image.png" alt="DP 방식 풀이 결과"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA - 싱글톤 패턴]]></title>
            <link>https://velog.io/@not_mean_min/JAVA-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@not_mean_min/JAVA-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 16 Dec 2024 09:39:22 GMT</pubDate>
            <description><![CDATA[<h2 id="싱글톤-패턴이란">싱글톤 패턴이란</h2>
<blockquote>
<p>자바에서 널리 사용되는 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 애플리케이션 내에서 단 하나만 존재하도록 보장하는 패턴이다.</p>
</blockquote>
<h3 id="특징">특징</h3>
<h4 id="1-메모리-효율성">1. 메모리 효율성</h4>
<p>싱글톤 패턴은 단 하나의 인스턴스만을 생성하고 재사용하므로 메모리 사용을 최소화할 수 있다. 특히 리소스가 많이 필요한 객체의 경우, 이는 상당한 메모리 절약으로 이어질 수 있다.</p>
<h4 id="2-전역-접근성">2. 전역 접근성</h4>
<p>전역 접근성은 프로젝트 내의 모든 객체가 이 객체에 접근할 수 있음을 의미한다. 싱글톤 인스턴스는 애플리케이션 전체에서 접근 가능하므로, 데이터 공유가 용이하다. 이는 데이터베이스 연결, 로깅, 캐시 등의 공유 리소스 관리에 특히 유용하다.
하지만 전역에서 접근 가능하므로 수많은 수정과 조작이 이뤄질 수 있으며, 이를 추적하는 것이 힘들어 의존성을 파악하기 어려워지고, 이에 따라서 리팩토링을 하기 어려워진다는 단점이 존재한다.</p>
<h4 id="3-일관된-상태-유지">3. 일관된 상태 유지</h4>
<p>하나의 인스턴스만 존재하므로 객체의 상태를 일관되게 유지할 수 있다. 이는 설정 정보나 애플리케이션의 전역 상태를 관리할 때 유용하다.
그러나 하나의 인스턴스만 존재하므로 유닛별 테스트를 어렵게 만들며, Mock 객체로 대체하기도 쉽지 않다. </p>
<h4 id="4-상속과-다형성의-제한">4. 상속과 다형성의 제한</h4>
<p>싱글톤 클래스는 일반적으로 private 생성자를 사용하므로 상속이 불가능하다. 이는 객체 지향 프로그래밍의 중요한 특성인 다형성을 제한할 수 있다.</p>
<h3 id="멀티-쓰레드에서의-싱글톤">멀티 쓰레드에서의 싱글톤</h3>
<h4 id="1-여러-개의-인스턴스-생성-가능">1. 여러 개의 인스턴스 생성 가능</h4>
<p>멀티 스레드에서 인스턴스가 없을 때 동시에 인스턴스를 불러오는 동작을 취한다면 각각의 스레드에서 인스턴스를 만드는 경우가 생길 수 있다.</p>
<h4 id="2-변수-값의-일관성-실패">2. 변수 값의 일관성 실패</h4>
<p>멀티 스레드에서 함수를 동시에 실행하게 된다면 일관되지 않은 값들의 결과물을 얻을 수 있다.</p>
<h3 id="구현">구현</h3>
<p>Java에서 싱글톤을 구현하기 위해 필요한 것은 다음과 같다.</p>
<ol>
<li>private 생성자: 외부에서 인스턴스를 직접 생성하지 못하도록 한다.</li>
<li>static private 변수: 클래스의 유일한 인스턴스를 저장한다.</li>
<li>static public 메소드: 인스턴스에 접근할 수 있는 유일한 방법을 제공한다.</li>
</ol>
<h4 id="구현방법">구현방법</h4>
<ol>
<li><p>Lazy Initialization(게으른 초기화)</p>
<pre><code class="language-java">public class Singleton {
 private static Singleton instance;

 private Singleton() {}

 public static Singleton getInstance() {
     if (instance == null) {
         instance = new Singleton();
     }
     return instance;
 }
}</code></pre>
<p>이 방법은 인스턴스가 필요할 때만 생성되므로 메모리를 절약할 수 있다. 하지만 멀티스레드 환경에서는 동시성 문제가 발생할 수 있으므로 아래의 방식이 권장된다.</p>
</li>
<li><p>Inner Static Helper Class (권장 방법)</p>
<pre><code class="language-java">public class Singleton {
 private Singleton() {}

 private static class SingletonHolder {
     private static final Singleton INSTANCE = new Singleton();
 }

 public static Singleton getInstance() {
     return SingletonHolder.INSTANCE;
 }
}</code></pre>
<p>JVM의 클래스 로딩 메커니즘과 클래스의 초기화 과정을 이용하여 스레드 안전성을 보장한다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA - 상속]]></title>
            <link>https://velog.io/@not_mean_min/JAVA-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@not_mean_min/JAVA-%EC%83%81%EC%86%8D</guid>
            <pubDate>Thu, 12 Dec 2024 12:33:07 GMT</pubDate>
            <description><![CDATA[<h2 id="상속이란">상속이란?</h2>
<p>상속은 자바의 객체지향 프로그래밍에서 중요한 개념으로, 특정 클래스(부모 클래스)의 속성과 메서드를 다른 클래스(자식 클래스)가 물려받아 사용하는 것을 말한다.</p>
<ul>
<li><h4 id="부모-클래스">부모 클래스</h4>
다른 클래스에게 자신의 속성과 기능을 물려줄 수 있는 클래스로, <code>super class</code> 또는 <code>base class</code>라고 불린다.</li>
<li><h4 id="자식-클래스">자식 클래스</h4>
부모 클래스로부터 속성과 기능을 상속받아 물려받은 클래스로, <code>subclass</code> 또는 <code>child class</code>라고 불린다.</li>
</ul>
<hr>

<ul>
<li><h4 id="장점">장점</h4>
코드 재사용성, 계층적 설계 가능.</li>
<li><h4 id="단점">단점</h4>
클래스 간의 강한 결합으로 인해 유연성이 떨어질 수 있음.</li>
</ul>
<hr>

<h3 id="구현방식">구현방식</h3>
<ul>
<li><h4 id="extends-키워드">extends 키워드</h4>
클래스 간의 상속에 사용된다.</li>
<li><h4 id="implements-키워드">implements 키워드</h4>
인터페이스 구현에 사용된다.<pre><code class="language-java">  class 자식클래스 extends 부모클래스 {
      // 추가적인 필드와 메서드 정의 가능
  }
  class 클래스명 implements 인터페이스1, 인터페이스2 {
      // 인터페이스의 메서드 구현 필요
  }</code></pre>
<hr>

</li>
</ul>
<h3 id="특징">특징</h3>
<ol>
<li>모든 자바 클래스는 자동으로 java.lang.Object 클래스를 상속받는다. 따라서 toString(), equals() 같은 메서드를 사용할 수 있다.</li>
<li>private 멤버를 제외한 상위 클래스의 모든 멤버가 하위 클래스에 상속된다.</li>
<li>자바는 단일 상속만을 지원한다. 즉, 한 클래스는 하나의 부모 클래스만 가질 수 있다. 이는 모호성을 방지하기 위해서다.<pre><code class="language-java"> class a extends b         // 가능
 class a extends b, c     // 불가능</code></pre>
<hr>

</li>
</ol>
<h3 id="예시">예시</h3>
<pre><code class="language-java">// 이름과 전화번호를 속성으로 가지는 기본 사람 클래스
class Person {
    protected String name;
    protected String phoneNumber;

    public Person(String name, String phoneNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
    }

    public String getName() {
        return name;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }
}

// 소속반 속성을 추가로 가지는 학생 클래스
class Student extends Person {
    private String className;

    public Student(String name, String phoneNumber, String className) {
        super(name, phoneNumber);
        this.className = className;
    }

    public String getClassName() {
        return className;
    }
}

// 과목 속성을 추가로 가지는 선생님 클래스
class Teacher extends Person {
    private String subject;

    public Teacher(String name, String phoneNumber, String subject) {
        super(name, phoneNumber);
        this.subject = subject;
    }

    public String getSubject() {
        return subject;
    }
}

// 메인 클래스
public class AcademyManagementSystem {
    public static void main(String[] args) {
        // 학생과 선생님 객체 생성
        Student student = new Student(&quot;홍길동&quot;, &quot;010-1234-5678&quot;, &quot;1반&quot;);
        Teacher teacher = new Teacher(&quot;김선생&quot;, &quot;010-2345-6789&quot;, &quot;수학&quot;);

        // 정보 출력
        System.out.println(&quot;=== 학생 정보 ===&quot;);
        System.out.println(&quot;이름: &quot; + student.getName());
        System.out.println(&quot;전화번호: &quot; + student.getPhoneNumber());
        System.out.println(&quot;반: &quot; + student.getClassName());
        // === 학생 정보 ===
        // 이름: 홍길동
        // 전화번호: 010-1234-5678
        // 반: 1반

        System.out.println(&quot;\n=== 선생님 정보 ===&quot;);
        System.out.println(&quot;이름: &quot; + teacher.getName());
        System.out.println(&quot;전화번호: &quot; + teacher.getPhoneNumber());
        System.out.println(&quot;담당 과목: &quot; + teacher.getSubject());
        // === 선생님 정보 ===
        // 이름: 김선생
        // 전화번호: 010-2345-6789
        // 담당 과목: 수학

    }
}
</code></pre>
]]></description>
        </item>
    </channel>
</rss>