11054 가장 긴 바이토닉 부분 수열
11054. 가장 긴 바이토닉 부분 수열 / c++ / Gold4 / 30분
문제
https://www.acmicpc.net/problem/11054
- 길이가 n인 수열 A가 주어진다.
- 임의의 지점 Ai를 기준으로 A0 < … < Ai-2 < Ai-1< Ai > Ai+1 > Ai+2 > … > An 의 형태를 취하는 수열을 바이토닉 수열이라고 한다.
- A에서 가장 긴 바이토닉 부분 수열의 길이는?
접근 방식
동적 프로그래밍
- dp[i] 는 Arr[i]가 가질 수 있는 가장 긴 증가 부분 수열의 길이
- dp[i]는 i보다 왼편의 수 중에서 ‘Arr[i]보다 작은 값 중 dp값이 가장 큰 값’ + 1
- revDp[i] 는 Arr[i]가 가질 수 있는 가장 긴 감소 부분 수열의 길이
- revDp[i]는 i보다 오른편의 수 중에서 ‘Arr[i]보다 작은 값 중 dp값이 가장 큰 값’ + 1
- dp[i] + revDp[i] 중 가장 큰 값을 반환
문제 풀이
#pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
using namespace std;
inline int MAX(int A, int B) { return A > B ? A : B; }
int N;
int arr[1'000];
int dp[1'000] = {};
int revDp[1'000] = {};
void Print()
{
for (int i = 0; i < N; ++i)
{
cout << dp[i] << ' ';
}
cout << '\n';
for (int i = 0; i < N; ++i)
{
cout << revDp[i] << ' ';
}
cout << '\n';
}
int DP(int num)
{
if (dp[num] == 0)
{
int maxNum = 0;
for (int i = 0; i < num; ++i)
{
if (arr[i] < arr[num])
maxNum = MAX(maxNum,DP(i));
}
dp[num] = maxNum + 1;
}
return dp[num];
}
int RevDP(int num)
{
if (revDp[num] == 0)
{
int maxNum = 0;
for (int i = N-1; i > num; --i)
{
if (arr[i] < arr[num])
maxNum = MAX(RevDP(i), maxNum);
}
revDp[num] = maxNum + 1;
}
return revDp[num];
}
int Find()
{
int maxNum = 1;
for (int i = 0; i < N; ++i)
{
maxNum = MAX(maxNum, dp[i] + revDp[i]);
}
return maxNum;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
cin >> N;
for (int i = 0; i < N; ++i)
{
cin >> arr[i];
DP(i);
}
for (int i = N - 1; i >= 0; --i)
{
RevDP(i);
}
cout << Find() - 1 << '\n';
//Print();
return 0;
}
다시 생각해 볼 점
- 역시 테이블로 죽 적어놓고 생각하니 문제풀이가 수월했다.
- 문제를 두개의 부분문제
- 증가 수열의 길이 dp
- 감소 수열의 길이 dp
로 나눠 놓고 나중에 1 + 2 의 값중 제일 큰 값 으로 합쳐서 푸는 사고방식이 중요했다.