baekjoon/DP
백준 20303번 : 할로윈의 양아치 [자바]
Meluu_
2025. 6. 9. 11:12
🧫 문제 분석
✔️ 출처
📖 문제

2차원 DP로 좀 많이 복잡하게 풀었다.
시간 제한 거의 닿을정도로 ㅋㅋ..
유니온-파인드Set + 넵샥 알고리즘으로 풀었다.
그리고 다른사람 풀이를 봤더니 1차원으로 충분히 가능하다는 것이다.
이번 풀이의 문제점
- Set으로 아이들의 값을 굳이 배열로 만듦
- 경로 압축을 했어도 중간 부모가 있음을 알지 못했음 (항상 최상위 부모를 찾을려면 find 할 것)
- 1차원 냅샥을 생각했지만 정방향으로만 생각해서 중복 연산될거라고 한 점
- - 이는 역방향으로 하면 중복연산하지 않는다.
🔅 문제 풀이
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.*;
import java.util.stream.IntStream;
public class Main {
// k 명 이상 뺏으면 안됨
static int[] parent;
static int[] candySum;
static int[] childCnt; // 소속된 집합의 아이들 수
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken()); // 아이들 수
int M = Integer.parseInt(st.nextToken()); // 친구 관계 수
int K = Integer.parseInt(st.nextToken()); // 울음소리가 공명하기 위한 최소 아이 수
parent = IntStream.range(0, N + 1).toArray();
candySum = new int[N + 1];
childCnt = new int[N + 1];
st = new StringTokenizer(br.readLine());
int[] candies = new int[N + 1];
for (int i = 1; i <= N; i++) {
candies[i] = Integer.parseInt(st.nextToken());
}
// 유니온 집합
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
union(a, b);
}
Set<Integer> set = new HashSet<>();
// 각 집합의 캔디 합을 저장
for (int i = 1; i <= N; i++) {
int area = find(i);
candySum[area] += candies[i];
childCnt[area]++;
set.add(area);
}
int[][] dp = new int[set.size() + 1][K];
int[] arr = new int[set.size() + 1];
int cnt = 1;
for (int value : set) {
arr[cnt++] = value;
}
for (int k = 1; k < K; k++) {
for (int i = 1; i < arr.length; i++) {
int idx = arr[i];
// 현 k명보다 적다면 이전 값의 최대값을 가짐
if (k < childCnt[idx]) {
dp[i][k] = Math.max(dp[i - 1][k], dp[i][k - 1]);
// 이전 최댓값 + 현재 캔디 개수 비교
} else {
dp[i][k] = Math.max(dp[i][k], candySum[idx] + dp[i - 1][k - childCnt[idx]]);
}
// 이전값과 비교해서 최댓값 갱신
dp[i][k] = Math.max(dp[i][k], Math.max(dp[i - 1][k], dp[i][k - 1]));
}
}
System.out.println(dp[arr.length - 1][K-1]);
}
private static void union(int a, int b) {
a = find(a);
b = find(b);
if (a < b) {
parent[b] = a;
} else {
parent[a] = b;
}
}
private static int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
}
❗ 오답노트 / 필요한 지식
- 배우자 성장하자