알고리즘 유형별 문제풀이

09.동적 프로그래밍 (Dynamic Programming) - 3

09.동적 프로그래밍 (Dynamic Programming) - 3

백준 11066번 - 파일 합치기


  • 접근
  • 1.풀고 싶은 가짜 문제(i ≤ j)
    • Dy[i][j] = i번 ~ j번 파일을 하나로 합치는 최소 비용
  • 2.가짜 문제를 풀면 진짜 문제를 풀 수 있는가
    • Dy 배열을 가득 채울 수만 있다면? 진짜 문제에 대한 대답은 Dy[1][K] 이다.
  • 3.초기값은 어떻게 되는가?
    • 초기값: 쪼개지 않아도 풀 수 있는 “작은 문제”들에 대한 정답
  • 4.점화식 구해내기
    • Dy[i][j] 계산에 필요한 탐색 경우를 공통점끼리 묶어 내기 (Partitioning)
    • 묶어낸 부분의 정답을 Dy 배열을 이용해서 빠르게 계산해보기
  • 구현

public class baekjoon11066 {


    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int T = Integer.parseInt(br.readLine());

        while (T-- > 0) {
            int K = Integer.parseInt(br.readLine());
            int[][] sum = new int[K + 1][K + 1];
            int[] num = new int[K + 1];

            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            for (int i = 1; i <= K; i++) {
                num[i] = Integer.parseInt(st.nextToken());
            }

            for (int i = 1; i <= K; i++) {
                for (int j = i; j <= K; j++) {
                    sum[i][j] = sum[i][j - 1] + num[j];
                }
            }

            int[][] Dy = new int[K + 1][K + 1];

            for (int len = 2; len <= K; len++) {
                for (int i = 1; i <= K - len + 1; i++) {
                    int j = i + len - 1;

                    Dy[i][j] = Integer.MAX_VALUE;

                    for (int k = i; k < j; k++) {
                        Dy[i][j] = Math.min(Dy[i][j], Dy[i][k] + Dy[k + 1][j] + sum[i][j]);
                    }

                }
            }

            System.out.println(Dy[1][K]);

        }


    }


}


백준 11049번 - 행렬 곱셈 순서


  • 구현

public class baekjoon11049 {


    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());

        int[][] Dy = new int[N][N];

        int[][] mat = new int[N][2];

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

        for (int i = 0; i < N - 1; i++) {
            Dy[i][i + 1] = mat[i][0] * mat[i][1] * mat[i + 1][1];
        }

        for (int len = 2; len < N; len++) {
            for (int i = 0; i + len < N; i++) {
                int j = i + len;
                Dy[i][j] = Integer.MAX_VALUE;
                for (int k = i; k < j; k++) {
                    Dy[i][j] = Math.min(Dy[i][j], Dy[i][k] + Dy[k + 1][j] + (mat[i][0] * mat[k][1] * mat[j][1]));
                }
            }
        }

        System.out.println(Dy[0][N - 1]);

    }


}


백준 10942번 - 팰린드롬?


  • 구현

public class baekjoon10942 {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());

        int[] arr = new int[n + 1];
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        for (int i = 1; i <= n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        boolean[][] dy = new boolean[n + 1][n + 1];

        for (int i = 1; i <= n; i++) {
            dy[i][i] = true;
        }

        for (int i = 1; i <= n - 1; i++) {
            if (arr[i] == arr[i + 1]) dy[i][i + 1] = true;
        }

        for (int i = 2; i < n; i++) {
            for (int j = 1; j <= n - i; j++) {
                if (arr[j] == arr[j + i] && dy[j + 1][j + i - 1]) {
                    dy[j][j + i] = true;
                }
            }
        }

        int m = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        while (m-- > 0) {
            st = new StringTokenizer(br.readLine(), " ");
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            sb.append(dy[x][y] ? "1\n" : "0\n");
        }

        System.out.println(sb);

    }


}


백준 1509번 - 팰린드롬 분할


  • 구현

public class baekjoon1509 {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String str = br.readLine();
        int n = str.length();

        char[] arr = str.toCharArray();
        boolean[][] check = new boolean[n + 1][n + 1];
        int[] dy = new int[n + 1];

        for (int i = 1; i <= n; i++) {
            dy[i] = Integer.MAX_VALUE;
        }

        for (int i = 1; i <= n; i++) {
            check[i][i] = true;
        }

        for (int i = 1; i <= n - 1; i++) {
            if (arr[i - 1] == arr[i]) check[i][i + 1] = true;
        }

        for (int i = 2; i < n; i++) {
            for (int j = 1; j <= n - i; j++) {
                if (arr[j - 1] == arr[j + i - 1] && check[j + 1][j + i - 1]) check[j][j + i] = true;
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                if (check[j][i]) {
                    dy[i] = Math.min(dy[i], dy[j - 1] + 1);
                }
            }
        }

        System.out.println(dy[n]);

    }

}


백준 15681번 - 트리와 쿼리


  • 접근
  • 작은 문제 -> 큰 문제
  • 1) 풀고 싶은 문제 정의
    • Dy[i] := i를 root로 하는 subtree의 정점 개수
  • 2) 초기값은 어떻게 되는가?
    • 초기값: “단말 노드”가 제일 작은 문제이다. (정점이 한 개 뿐이라서)
  • 3) 점화식 구해내기
    • Rooted Tree이므로 부모, 자식 관계가 존재한다.
    • 자식 노드에 대해 문제를 해결한다면, 부모 노드는 그걸 이용해서 문제를 풀면 된다.
    • Dy[부모 노드] = ∑ Dy[자식 노드들] + 1
  • 시간, 공간 복잡도 계산하기
  • Rooted Tree 문제를 DP 로 풀 경우 대부분 DFS 한 번에 해결한다. 따라서 DFS의 시간복잡도인 O(V + E)=O(N) 만에 문제를 풀 수 있다.

  • 구현

public class baekjoon15681 {


    static int[] Dy;
    static int N, R, Q;
    static ArrayList<Integer>[] graph;

    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());
        R = Integer.parseInt(st.nextToken());
        Q = Integer.parseInt(st.nextToken());

        graph = new ArrayList[N + 1];
        Dy = new int[N + 1];

        for (int i = 1; i <= N; i++) {
            graph[i] = new ArrayList<>();
        }

        for (int i = 1; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            graph[x].add(y);
            graph[y].add(x);
        }

        dfs15681(R, -1);

        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= Q; i++) {
            int U = Integer.parseInt(br.readLine());
            sb.append(Dy[U]).append('\n');
        }
        System.out.println(sb);
    }

    private static void dfs15681(int x, int prev) {
        Dy[x] = 1;
        for (int y : graph[x]) {
            if (prev == y) continue;
            dfs15681(y, x);
            Dy[x] += Dy[y];
        }
    }

}



백준 1949번 - 우수 마을


  • 정답의 최대치
    일단 모든 마을에 최대 인원이 존재해야 할 것이므로 모든 마을에 1만 명의 주민이 존 재할 것이다. 마을을 최대한 많이 고르기 위해서는 최대 개수(1만 개)의 마을이 일렬로 존재해서 약 9,999 개의 마을을 선택할 경우이다. 즉, 약 1억 명을 고르는 것이 최대이므로 Integer 범위임이 보장된다.

  • 접근
  • 작은 문제 -> 큰 문제
  • 1) 풀고 싶은 문제 정의
    • Dy[i][0] := i를 root로 하는 subtree에서 root를 선택하지 않고서 가능한 최대 주민 수
    • Dy[i][1] := i를 root로 하는 subtree에서 root를 선택하고서 가능 한 최대 주민 수
  • 2) 가짜 문제를 풀면 진짜 문제를 풀 수 있는가?
    • Dy 배열을 가득 채울 수만 있다면? 진짜 문제에 대한 대답은 실제 루트를 고르 거나 안 고르는 것 중 최대이므로 max(Dy[1][0] ,Dy[1][1]) 이다.
  • 3) 초기값은 어떻게 되는가?
    • 초기값: “단말 노드”가 제일 작은 문제이다. (정점이 한 개 뿐이라서)
  • 4) 점화식 구해내기
    • Rooted Tree이므로 부모, 자식 관계가 존재한다.
    • 자식 노드에 대해 문제를 해결한다면, 부모 노드는 그걸 이용해서 문제를 풀면 된다.
  • 시간, 공간 복잡도 계산하기
    Rooted Tree 문제를 DP 로 풀 경우 대부분 DFS 한 번에 해결한다. 따라서 DFS의 시간복잡도인 O(V + E)=O(N) 만에 문제를 풀 수 있다.

  • 구현

public class baekjoon1949 {

  static int N;
  static int[] A;
  static ArrayList<Integer>[] tree;
  static int[][] Dy;

  public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    N = Integer.parseInt(br.readLine());
    A = new int[N + 1];
    tree = new ArrayList[N + 1];
    Dy = new int[N + 1][2];

    StringTokenizer st = new StringTokenizer(br.readLine(), " ");
    for (int i = 1; i <= N; i++) {
      A[i] = Integer.parseInt(st.nextToken());
      tree[i] = new ArrayList<>();
    }

    for (int i = 1; i < N; i++) {
      st = new StringTokenizer(br.readLine(), " ");
      int x = Integer.parseInt(st.nextToken());
      int y = Integer.parseInt(st.nextToken());

      tree[x].add(y);
      tree[y].add(x);
    }

    dfs1949(1, -1);

    System.out.println(Math.max(Dy[1][0], Dy[1][1]));

  }

  private static void dfs1949(int x, int prev) {
    Dy[x][1] = A[x];

    for (int y : tree[x]) {
      if (y == prev) continue;

      dfs1949(y, x);

      Dy[x][0] += Math.max(Dy[y][0], Dy[y][1]);
      Dy[x][1] += Dy[y][0];
    }


  }
}




© 2020. All rights reserved.

SIKSIK