구간 나누기(DP)

2023. 5. 4. 15:39C++/알고리즘

 

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

 

2228번: 구간 나누기

N(1 ≤ N ≤ 100)개의 수로 이루어진 1차원 배열이 있다. 이 배열에서 M(1 ≤ M ≤ ⌈(N/2)⌉)개의 구간을 선택해서, 구간에 속한 수들의 총 합이 최대가 되도록 하려 한다. 단, 다음의 조건들이 만족되

www.acmicpc.net

 

Top-Down 방식의 다이나믹 프로그래밍 기법을 사용한다.

 

DP[N번째 숫자][M개의 구간]으로 정의한다.
DP에 저장되는 값은 N번째 숫자까지 M개의 구간을 만들때 가능한 숫자의 최대합.

 

M개의 구간을 만들어야 하는 상황에서 N번째 숫자에 도달했다면 가능한 경우의 수는 다음과 같다.

 

1. N번째 숫자를 M번째 구간에 포함시키지 않는다.

-> DP[N][M] = DP[N-1][M]

 

2. N번째 숫자를 M번째 구간에 포함시키는 모든 경우의 수를 판단한다.

0~N의 숫자에서 M개의 구간의 구간합 =
0~K의 숫자에서 M-1개의 구간의 구간합 + K~N의 숫자를 1개의 구간으로 만들고 이 구간의 구간합

-> DP[N][M] = DP[K-2][M-1] + Sum(K ~ N), (0 < K <= N)

 

다음과 같은 점화식을 토대로 재귀함수를 호출해주면 된다.

#include <iostream>
using namespace std;

int n, m, num[100], sum[100], dp[100][100];

int solve(int idx, int seq)
{
	if (idx + 1 < 2 * seq - 1)return -987654321;
	if (seq <= 0) return 0;
	if (dp[idx][seq]) return dp[idx][seq];

	int ret = -987654321;
	ret = max(ret, solve(idx - 1, seq));
	for (int i = idx; i >= 0; i--)
	{
		ret = max(ret, solve(i - 2, seq - 1) + sum[idx] - sum[i - 1]);
	}

	return dp[idx][seq] = ret;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n >> m;

	for (int i = 0; i < n; i++) {
		cin >> num[i];
		if (i == 0) sum[i] = num[i];
		else sum[i] = sum[i - 1] + num[i];
	}

	cout << solve(n - 1, m) << '\n';
}

'C++ > 알고리즘' 카테고리의 다른 글

공통 부분 문자열(DP)  (1) 2023.05.10
Longest Common Subsequence/Substring(LCS)  (1) 2023.05.10
고층빌딩(DP)  (1) 2023.05.02
집합의 표현(Union-Find)  (0) 2023.01.02
외판원 순회(DP, BitMask)  (0) 2022.12.22