알고리즘/백준

[백준] 20303번 : 할로윈의 양아치 - 자바(JAVA)

Junuuu 2022. 7. 27. 00:01
반응형

https://www.acmicpc.net/problem/20303

 

20303번: 할로윈의 양아치

첫째 줄에 정수 $N$, $M$, $K$가 주어진다. $N$은 거리에 있는 아이들의 수, $M$은 아이들의 친구 관계 수, $K$는 울음소리가 공명하기 위한 최소 아이의 수이다. ($1 \leq N \leq 30\ 000$, $0 \leq M \leq 100\ 000$,

www.acmicpc.net

 

문제 해석

스브러스는 사탕을 뺏으려고 한다.

이때 스브러스는 사탕을 뺏을 때 한 아이의 사탕을 뺏으면 그 아이 친구들의 사탕도 모조리 뺏어버립니다.

이때 K명 이상의 아이들의 뺏지 않으면서 최대로 뺏을 수 있는 사탕의 양을 구하세요

 

문제 풀이전 설계

거리에 있는 아이들의 수 N = 30,000

아이들의 친구 관계 수 M = 100,000

K = 3,000

 

 

1. DFS/ BFS /Union Find 어떤것을 쓰던 우선 친구 그룹을 지어줍니다.

 

2. 친구그룹별로 사탕의 개수와 , 연결되어있는 친구들의 수가 도출됩니다.

 

3. 이를 토대로 최대 몇 명을 뺏을 수 있는지 탐색합니다.

 

문제 풀이하면서

2번까지는 순조롭게 잘 되었습니다.

이후에 3번을 2중반복문을통해 하면 해결되겠지 하고 제출하니 2%에 틀렸습니다가 나옵니다.

 

곰곰이 생각해보니 단순히 2개로만 최댓값을 비교하면 안 됩니다.

만약 3개, 4개를 합쳐서 최댓값이 되는 경우를 생각해보면 오답이 나오는 이유를 알 수 있습니다.

 

따라서 0-1 배낭 문제처럼 값을 계속 기록해나가면서 최댓값을 경신해나가야 정답을 찾을 수 있습니다.

 

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main_20303_할로윈의양아치 {

    static List<SteelInfo> canSteel = new ArrayList<>();
    static List<ArrayList<Integer>> links = new ArrayList<>();
    static int[] childs;
    static boolean[] visited;
    static int N, M, K;

    static class SteelInfo {
        int sum;
        int childCount;

        public SteelInfo(int sum, int childCount) {
            this.sum = sum;
            this.childCount = childCount;
        }

        @Override
        public String toString() {
            return "SteelInfo{" +
                    "sum=" + sum +
                    ", childCount=" + childCount +
                    '}';
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        childs = new int[N + 1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            childs[i] = Integer.parseInt(st.nextToken());
        }

        for (int i = 0; i <= N; i++) {
            links.add(new ArrayList<>());
        }

        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            links.get(a)
                 .add(b);
            links.get(b)
                 .add(a);
        }

        //입력끝, 양방향 연결그래프 생성

        visited = new boolean[N + 1];

        for (int i = 1; i <= N; i++) {
            if (visited[i]) {
                continue;
            }
            SteelInfo result = steelCandy(i);
            if (result.childCount < K) {
                canSteel.add(result);
            }
        }

        int size = canSteel.size();


        int[][] dp = new int[2][K];

        for (int i = 0; i < canSteel.size(); i++) {
            SteelInfo now = canSteel.get(i);
            int childCount = now.childCount;
            int sum = now.sum;

            for (int j = 0; j < K; j++) {
                if (j >= childCount && dp[0][j - childCount] + sum > dp[0][j])
                    dp[1][j] = dp[0][j - childCount] + sum;
                else
                    dp[1][j] = dp[0][j];
            }
            dp[0] = dp[1].clone();
        }
        System.out.println(dp[1][K - 1]);
        

    }

    private static SteelInfo steelCandy(int childIndex) {

        Queue<Integer> bfsQ = new LinkedList<>();
        bfsQ.add(childIndex);
        visited[childIndex] = true;

        int sum = 0;
        int count = 0;
        SteelInfo result = null;
        while (!bfsQ.isEmpty()) {

            Integer curIndex = bfsQ.poll();
            sum += childs[curIndex];
            count++;

            for (Integer cur : links.get(curIndex)) {
                if (visited[cur]) {
                    continue;
                }

                bfsQ.add(cur);
                visited[cur] = true;
            }
        }
        result = new SteelInfo(sum, count);

        return result;

    }
}