(백준/C++) 11053_가장 긴 바이토닉 부분 수열
가장 긴 바이토닉 부분 수열 문제를 풀기 전에 가장 긴 증가하는 부분 수열 문제를 풀고 오는 것을 추천 드립니다.
바이토닉 수열은 어떤 수열이 증가하다가 감소하는 형태를 갖는 수열입니다.
즉, 증가하다가 감소하게 되는 부분 수열들 중 가장 긴 값을 찾아야 합니다.
우선, 동적 계획법(Dynamic Programming, DP)이란, 큰 문제를 작은 문제들로 나누어 풀어나가고 작은 문제의 해를 저장해 다시 계산하지 않고 사용하는 방식입니다.
동적 계획법 문제는 최적 부분 구조와 중복된 부분 문제의 두 가지 속성을 가집니다. 이 두 가지 속성에 대해서 생각해보고 가겠습니다.
- 최적 부분 구조: 가장 긴 바이토닉 부분 수열 문제에서 최적 부분 구조는 다음과 같이 두 부분으로 나뉠 수 있습니다.
- 증가 부분 수열: 각 요소에서 이전 요소들과 비교하여 증가하는 부분 수열의 최대 길이를 찾습니다.
- 감소 부분 수열: 각 요소에서 이후 요소들과 비교하여 감소하는 부분 수열의 최대 길이를 찾습니다.
즉, 바이토닉 수열의 길이를 최대화하려면, 특정 위치에서의 증가하는 부분 수열의 최대 길이와 감소하는 부분 수열의 최대 길이를 결합하여 계산해야 합니다.
- 중복된 부분 문제: 각 요소에 대해 이전/이후 요소들과의 최대 길이를 계산할 때, 이전에 이미 계산한 결과를 재사용하게 됩니다.
접근법
1. 초기화
이 문제에서도 각 요소를 첫 번째로 시작하는 부분 수열이라면 길이가 1이기 때문에 1로 초기화합니다.
2. 점화식 찾기
이 문제는 점화식을 두 개 찾을 수 있습니다.
증가하는 부분 수열의 경우에는 현재 요소를 마지막으로 하는 가장 긴 증가하는 부분 수열의 길이를 구해야 합니다. 이는 i번째 요소보다 작은 모든 이전 요소(j < i)에 대해 가장 긴 부분 수열의 길이를 찾아야 합니다
반대의 경우에도 마찬가지로 현재 요소를 시작으로 하는 가장 긴 감소하는 부분 수열의 길이를 구해야 합니다.
풀이
만약 가장 긴 증가하는 부분 수열 문제를 풀고 왔다면 어렵지 않게 점화식을 찾고, 문제를 풀 수 있을 겁니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <vector>
using namespace std;
vector<int> seq;
vector<vector<int>> length;
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int N;
cin >> N;
seq.resize(N);
length.resize(N, vector<int>(2, 1));
for (int i = 0; i < N; i++)
cin >> seq[i];
//i를 마지막으로 하는 제일 긴 수열을 계산한다.
for (int i = 1; i < N; i++)
{
for (int j = 0; j < i; j++)
{
if (seq[i] > seq[j])
length[i][0] = max(length[i][0], length[j][0] + 1);
}
}
//i를 시작으로 하는 제일 긴 수열을 계산한다.
for (int i = N - 1; i >= 0; i--)
{
for (int j = N - 1; j > i; j--)
{
if (seq[i] > seq[j])
length[i][1] = max(length[i][1], length[j][1] + 1);
}
}
int lengthMax = 0;
for(int i = 0; i < N; i++)
{
lengthMax = max(lengthMax, length[i][0] + length[i][1] - 1);
}
cout << lengthMax;
}
참고
이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.