Baekjoon

20303 - 할로윈의 양아치 JAVA

kjy0349 2024. 2. 27. 14:57

1차원 냅색 + 그래프 개념이 포함된 문제이다.

첫번째 아이디어 : 한 명이 사탕을 뺏기면 친구들도 다 뺏긴다 -> BFS 탐색으로 친구들끼리 묶을 수 있다.

친구들끼리 묶어 하나의 입력으로 만든다. 그 후, 배낭 문제처럼 풀이한다.

예시를 들어보자.

각 사람마다 사탕의 개수를 갖고 있는 배열을 candies[]라고 하자.

N번째 사람을 생각해봤을 때, 해당 사람과 연결된 친구가 없을 경우 무게가 1이고 사탕의 개수가 candies[N]인 배낭 하나로 생각할 수 있다. (이 부분을 생각하지 못해서 틀렸었다.. 친구 관계가 없는 사람도 있음)

N번째 사람이 연결된 친구가 있을 경우, (자신 + 친구들의 수)만큼의 무게이고 사탕의 개수가 (candies[N] + candies[친구들의 idx])인 배낭 하나로 만들 수 있다.

++ 추가적으로 사람의 수와 친구들의 수가 꽤 많기 때문에, 2차원 배낭 문제로 해결하게되면 메모리를 많이 쓰게 된다. 따라서 K - 1부터 탐색해 배낭 문제를 풀이하는 방식을 채택하면, 메모리 사용량을 많이 줄일 수 있다.

배낭으로 사용할 클래스

    static class Bag {
        int weight;
        int candy;
        Bag(int weight, int candy) {
            this.weight = weight;
            this.candy = candy;
        }

        @Override
        public String toString() {
            return weight + " " + candy;
        }
    }

각 친구들과의 관계를 이용해 모두 배낭으로 만듦

-> adjList에는 N + 1만큼의 ArrayList를 가지고 있다.(1번부터 시작하기 위함)

public static ArrayList<Bag> findBags(int[] candies, ArrayList<ArrayList<Integer>> adjList) {
        // 가방 찾기
        ArrayList<Bag> retArr = new ArrayList<>();
        boolean[] visited = new boolean[candies.length]; // 이미 가방으로 만든 친구들을 제외하기위한 visited 배열
        for (int i = 1; i < adjList.size(); i++) {
            if (!visited[i]) {
                visited[i] = true;
                int candy = candies[i];
                int weight = 1;
                Queue<Integer> que = new ArrayDeque<>();
                que.offer(i);
                while (!que.isEmpty()) {
                    int target = que.poll();
                    for (int elem : adjList.get(target)) {
                        if (!visited[elem]) {
                            que.offer(elem);
                            visited[elem] = true;
                            weight++;
                            candy += candies[elem];
                        }
                    }
                }
                retArr.add(new Bag(weight, candy));
            }
        }
        for (int i = 1; i < visited.length; i++) { // 친구가 없는 사람들은 무게가 1인 배낭으로 꼭 변환해주어야 한다.
            if (!visited[i]) {
                visited[i] = true;
                retArr.add(new Bag(1, candies[i]));
            }
        }
        return retArr;
    }

 

입력 받기 + 배낭으로 만든 후 배낭 문제 풀이 형식으로 답안 출력

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());
        st = new StringTokenizer(br.readLine());
        ArrayList<ArrayList<Integer>> adjList = new ArrayList<ArrayList<Integer>>();
        int[] candies = new int[N + 1];
        adjList.add(new ArrayList<>());
        for (int i = 1; i <= N; i++) { // candies -> 해당 인간이 갖고 있는 사탕 수, adjList -> 친구 관계
            candies[i] = Integer.parseInt(st.nextToken());
            adjList.add(new ArrayList<>());
        }
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            adjList.get(start).add(end);
            adjList.get(end).add(start);
        }

        ArrayList<Bag> bags = findBags(candies, adjList); // K는 들 수 있는 가방 최대 무게
        int[] dp = new int[K]; // K 무게를 만든다. N번째 가방을 써서
        for (int bIdx = 0; bIdx < bags.size(); bIdx++) {
            Bag bag = bags.get(bIdx);
            for (int i = K - 1; i >= bag.weight; i--) {
                dp[i] = Math.max(dp[i], dp[i - bag.weight] + bag.candy);
            }
        }
        System.out.println(dp[K - 1]);
    }