mojo's Blog

[백준 12013] 248 게임 본문

백준/Dynamic Programming

[백준 12013] 248 게임

_mojo_ 2022. 7. 28. 10:07

문제 링크 : 12013번: 248 게임 (acmicpc.net)

 

12013번: 248 게임

이 예에서, Bessie는 두 번째와 세 번째에 있는 1을 2로 합친다. (1 2 2) 두 번째와 세 번째에 있는 2를 3으로 합친다. (1 3) 첫 번째와 두 번째 1을 합치는 것은 최적해가 아님을 주의해라.

www.acmicpc.net

 

Bessie 는 두 개의 인접한 수가 같을 때 (인접한 수 + 1) 로 합칠 수 있다고 한다.

즉 이러한 작업을 계속 하여 더이상 진행이 되지 않도록 하는 수열에 대해서 가장 큰 수를 구하는 문제이다.

위 문제를 다이나믹 프로그래밍으로 \(O(N^{3})\) 시간복잡도로 해결했다.

 

 

※ 접근 방법

1. dp[l][r] 을 [l, r] 범위 내에 얻을 수 있는 최대 점수라고 설정하였다.

    그렇다면 자연스럽게 기본 케이스는 다음과 같이 가져갈 수 있다.

	/* 1. base case */
	for (int i = 1; i <= N; i++) {
		dp[i][i] = A[i];
	}

 

2. [l, r] 범위에서 r - l > 1 일 때 어떻게 optimal substructure 를 구상할 것인지에 대한 고민이 필요했다.

    즉, 이전의 memoziation 으로 설정된 기본 케이스를 가지고 이어가야 한다.

    그러면 문제에서 주어진 수열 [1, 1, 1, 2] 를 가지고 규칙을 찾아본다.

 

  • dp[1][2] = 2 : dp[1][1] == dp[2][2]  ~>  (dp[1][1] + 1) = 2 
  • dp[3][4] = 2 : dp[3][3] != dp[4][4]  ~>  max(dp[3][3], dp[4][4]) = 2 
  • dp[1][3] = 2 : dp[1][1] != dp[2][3], dp[1][2] != dp[3][3]  ~>  max(max(dp[1][1], dp[2][3]), max(dp[1][2], dp[3][3])) = 2
  • dp[2][4] = 3 : dp[2][3] == dp[4][4]  ~>  (dp[2][3] + 1) = 3

 

정리하자면, 위와 같은 순서로 memoziation 을 비교하면서 같을 경우와 다를 경우에 대한 식을 구할 수 있다.

 

\( dp[l][r]_{l \leq k \leq r-1} 
= \begin{cases}
 dp[l][k] + 1 & \text{if dp[l][k] = dp[k+1][r]}  \\
 max(dp[l][k], dp[k+1][r]) & \text{if dp[l][k] != dp[k+1][r]}  
\end{cases} \)

 

이렇게 설계를 하고 코드 구현을 한 다음에 기분좋게 제출을 한 결과 틀렸다.

 

 

그렇다. 

고려해야 할 게 더 있다.

 

3. 수열 [1, 2, 1, 2] 를 2 번에서 설계된 구조를 가지고 구해보도록 하자.

 

  • dp[1][2] = dp[2][3] = dp[3][4] = 2 
  • dp[1][3] = 2 : dp[1][1] != dp[2][3], dp[1][2] != dp[3][3]  ~>  max(max(dp[1][1], dp[2][3]), max(dp[1][2], dp[3][3])) = 2
  • dp[2][4] = 3 : dp[2][2] != dp[3][4], dp[2][3] != dp[4][4]  ~> max(max(dp[2][2], dp[3][4]), max(dp[2][3], dp[4][4])) = 3

 

dp[2][4] 의 값이 잘못된 것을 알 수 있다.

즉, max(dp[2][2], dp[3][4]) 에서 dp[3][4] 가 2 인것은 맞지만 합쳐져서 2 가 된 것인지 아닌지에 대한 정보가 없다.

따라서 2차원 배열을 하나 더 선언하였다. 

check[l][r] = [l, r] 범위 내에 합쳐진 결과인지에 대한 정보 (true/false)

 

 

위와 같이 check 배열을 추가 선언하여 해결할 수 있다.

 

풀이 코드

#define _CRT_SECURE_NO_WARNINGS
#include <string>
#include <stdio.h>
#include <math.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <set>

#define INF 1000000000
#define endl '\n'
#define ll long long

using namespace std;

int N;
int A[250];
int dp[250][250];
bool check[250][250];

void input() {
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
}

void solution() {
	int r;

	/* 1. base case */
	for (int i = 1; i <= N; i++) {
		dp[i][i] = A[i];
		check[i][i] = true;
	}

	/* 2. dynamic programming */
	for (int gap = 1; gap <= N - 1; gap++) {
		for (int l = 1; l <= N - gap; l++) {
			r = l + gap;
			for (int k = 0; k <= gap - 1; k++) {
				if (dp[l][l + k] == dp[l + k + 1][r]
					&& check[l][l + k] && check[l + k + 1][r]) {
					dp[l][r] = max(dp[l][r], dp[l][l + k] + 1);
					check[l][r] = true;
				}
				else dp[l][r] = max(dp[l][r], max(dp[l][l + k], dp[l + k + 1][r]));
			}
		}
	}

	/* 3. Find solution */
	cout << dp[1][N];
}

int main()
{
	ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	input();
	solution();

	return 0;
}

 

'백준 > Dynamic Programming' 카테고리의 다른 글

[백준 1949] 우수 마을  (0) 2022.03.24
[백준 2342] Dance Dance Revolution  (0) 2021.12.28
[백준 1509] 팰린드롬 분할  (0) 2021.12.23
[백준 10251] 운전 면허 시험  (0) 2021.12.06
[백준 1937] 욕심쟁이 판다  (0) 2021.11.15
Comments