https://www.acmicpc.net/problem/2228
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 IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
int n = Integer.parseInt(tokenizer.nextToken());
int m = Integer.parseInt(tokenizer.nextToken());
int[] prefix = new int[n+1];
int[][] dp = new int[n+1][m+1]; //dp[i][j]: i번째 숫자까지 j개의 부분합으로 나눴을 때 최대 구간합
for(int i = 0; i <= n; i++) {
for(int j = 1; j <= m; j++) {
dp[i][j] = -32768 * 101;
}
}
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + Integer.parseInt(reader.readLine());
}
dp[1][1] = prefix[1];
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (j == 1) {
dp[i][j] = Math.max(dp[i][j], prefix[i]);
}
for (int k = 0; k < i - 1; k++) {
dp[i][j] = Math.max(dp[i][j], dp[k][j - 1] + prefix[i] - prefix[k + 1]);
}
}
}
System.out.println(dp[n][m]);
}
}
1. 문제
N(1 ≤ N ≤ 100)개의 수로 이루어진 1차원 배열이 있다. 이 배열에서 M(1 ≤ M ≤ ⌈(N/2)⌉)개의 구간을 선택해서, 구간에 속한 수들의 총 합이 최대가 되도록 하려 한다. 단, 다음의 조건들이 만족되어야 한다.
- 각 구간은 한 개 이상의 연속된 수들로 이루어진다.
- 서로 다른 두 구간끼리 겹쳐있거나 인접해 있어서는 안 된다.
- 정확히 M개의 구간이 있어야 한다. M개 미만이어서는 안 된다.
N개의 수들이 주어졌을 때, 답을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 두 정수 N, M이 주어진다. 다음 N개의 줄에는 배열을 이루는 수들이 차례로 주어진다. 배열을 이루는 수들은 -32768 이상 32767 이하의 정수이다.
출력
첫째 줄에 구간에 속한 수들의 총 합의 최댓값을 출력한다.
2. 풀이
DP 문제이다. dp[i][j]를 다음과 같이 정의해보자.
- dp[i][j]: i 번째 숫자까지 j 개의 구간합으로 나눴을 때의 최대 부분합
i개까지의 숫자로 j 개의 구간합을 나눴다고 가정하자. 여기서 숫자가 하나 더 추가된다면 j+1개의 구간합으로 나눈 경우는 i-1까지의 j개의 구간합에 i+1을 추가한 경우, i-2개까지의 j개의 구간합에 i, i+1번째 수로 구간합을 하나 만드는 경우 ... 이렇게 하여 최댓값이 dp에 들어간다. 이걸 기반으로 문제를 풀어보자.
int n = Integer.parseInt(tokenizer.nextToken());
int m = Integer.parseInt(tokenizer.nextToken());
int[] prefix = new int[n+1];
int[][] dp = new int[n+1][m+1]; //dp[i][j]: i번째 숫자까지 j개의 부분합으로 나눴을 때 최대 구간합
for(int i = 0; i <= n; i++) {
for(int j = 1; j <= m; j++) {
dp[i][j] = -32768 * 101;
}
}
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + Integer.parseInt(reader.readLine());
}
dp[1][1] = prefix[1];
우선 구간합을 이용하는 문제이므로 배열에서 prefix sum을 구하여 저장해준다. 그리고 dp를 초기화한다.
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (j == 1) {
dp[i][j] = Math.max(dp[i][j], prefix[i]);
}
for (int k = 0; k < i - 1; k++) {
dp[i][j] = Math.max(dp[i][j], dp[k][j - 1] + prefix[i] - prefix[k + 1]);
}
}
}
이후 위 규칙에 맞춰 계산을 한다. 만약 i번째 수를 포함하지 않는 경우 dp[i][j]=dp[i-1][j]이다. 또, j가 1이면 사용할 구간합 공식(prefix[i]-prefix[k+1])으로 구할 수 없으므로 따로 계산을 해준다. 이후 k에 대해 점화식을 적용하여 j-1개의 구간합에 새 구간합을 추가하여 가능한 경우 중 최댓값을 구하여 출력한다.
'Coding Test' 카테고리의 다른 글
백준 15558 점프 게임 (Java) (0) | 2024.11.14 |
---|---|
백준 1726 로봇 (Java) (0) | 2024.11.13 |
Leetcode 3011 Find if Array Can Be Sorted (Java) (0) | 2024.11.06 |
Leetcode 3163 String Compression III (Java) (0) | 2024.11.04 |
Leetcode 1277 Count Square Submatrices with All Ones (Java) (0) | 2024.11.01 |