Algorithm

[Algo] 백준 28071 승형이의 사탕 사기 JAVA

조핑구 2024. 5. 8. 13:39

문제 바로가기 : https://www.acmicpc.net/problem/28071


코드

bfs

메모리 : 22780KB

시간 : 336ms

언어 : Java 11

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static class Node {
        int num, depth;

        Node(int num, int depth) {
            this.num = num;
            this.depth = depth;
        }
    }

    public static void main(String[] args) throws NumberFormatException, 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());
        int[] candy = new int[N];
        st = new StringTokenizer(br.readLine());
        Queue<Node> q = new LinkedList<>();
        int maxcandy = 0;
        for (int i = 0; i < N; i++) {
            candy[i] = Integer.parseInt(st.nextToken());
            q.add(new Node(candy[i], 1));
            maxcandy = Math.max(maxcandy, candy[i]);
        }
        int max = 0;
        boolean[] check = new boolean[maxcandy * M + 1];
        while (!q.isEmpty()) {
            Node n = q.poll();
            if (n.num % K == 0) {
                max = Math.max(max, n.num);
            }
            if (n.depth == M) {
                continue;
            }
            for (int i = 0; i < N; i++) {
                if (!check[n.num + candy[i]]) {
                    check[n.num + candy[i]] = true;
                    q.add(new Node(n.num + candy[i], n.depth + 1));
                }
            }
        }
        System.out.println(max);
    }
}

풀이

승형이의 사탕 사는 조합을 bfs로 구할 수 있다.
승형이가 살 수 있는 사탕의 최대 개수는 (최대상자*상자에 들어 있을 수 있는 최대 사탕 수) 이다.
따라서 bfs를 돌면서 K로 나눠지는 경우를 모두 찾아서 최댓값을 찾으면 된다.


코드

배낭 알고리즘(DP)

메모리 : 15640KB

시간 : 424ms

언어 : Java 11

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

public class Main {
    public static void main(String[] args) throws NumberFormatException, 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());
        int[] candy = new int[N];
        st = new StringTokenizer(br.readLine());
        int[][] dp = new int[M + 1][K];
        for (int i = 0; i < N; i++) {
            candy[i] = Integer.parseInt(st.nextToken());
            dp[1][candy[i] % K] = Math.max(candy[i], dp[1][candy[i] % K]);
        }

        int ans = 0;
        for (int i = 1; i < M; i++) {
            for (int j = 0; j < N; j++) {
                for (int k = 0; k < K; k++) {
                    if (dp[i][Math.floorMod(k - candy[j], K)] != 0) {
                        dp[i + 1][k] = Math.max(dp[i + 1][k], dp[i][Math.floorMod(k - candy[j], K)] + candy[j]);
                    }
                }
            }
        }
        for (int i = 0; i <= M; i++) {
            // 나머지가 0인것 중 가장 큰 것
            ans = Math.max(dp[i][0], ans);
        }
        System.out.println(ans);
    }
}

풀이

너무너무..어려웠다 DP는 어려워ㅠ
K로 나눠지는 수 만큼의 사탕을 가져야 한다는 것을 나머지를 이용해 해결했다.

나머지가 K-n인 사탕과 나머지가 n인 사탕을 더하면 결국 K의 배수가 된다
이 아이디어를 이용해 점화식을 세우면 다음과 같다

    for (int i = 1; i < M; i++) {
            for (int j = 0; j < N; j++) {
                for (int k = 0; k < K; k++) {
                    // 나머지를 맞출 짝이 없다면 넘겨야 한다.
                    if (dp[i][Math.floorMod(k - candy[j], K)] != 0) {
                        //i개의 사탕상자를 샀을 때 k개의 나머지가 나오는 경우는 =
                        //전에 기억했던 값, (현재의 찾을 나머지 k - 지금 산 사탕상자)%K + 지금 산 사탕상자   중 큰 값
                        dp[i + 1][k] = Math.max(dp[i + 1][k], dp[i][Math.floorMod(k - candy[j], K)] + candy[j]);
                    }
                }
            }
        }

dp[i][Math.floorMod(k - candy[j], K)] 에서 Math.floorMod(k - candy[j], K) 는 현재 산 candy[i] 와 짝을 지을 전 칸의 나머지를 찾는 것이다.
dp배열을 완성하고 나면 나머지가 0인 것들 중에서 최댓값을 찾아주면 된다.