Algorithm/문제풀이

[다이나믹 프로그래밍] 가장 긴 바이토닉 부분 수열

lee308812 2018. 8. 11. 00:45

문제 링크 : https://www.acmicpc.net/problem/11054


 

시간 제한메모리 제한제출정답맞은 사람정답 비율
1 초256 MB51342643215653.806%

문제

수열 S가 어떤 수 Sk를 기준으로 S1 < S2 < ... Sk-1 < Sk > Sk+1 > ... SN-1 > SN을 만족한다면, 그 수열을 바이토닉 수열이라고 한다.

예를 들어, {10, 20, 30, 25, 20}과 {10, 20, 30, 40}, {50, 40, 25, 10} 은 바이토닉 수열이지만,  {1, 2, 3, 2, 1, 2, 3, 2, 1}과 {10, 20, 30, 40, 20, 30} 은 바이토닉 수열이 아니다.

수열 A가 주어졌을 때, 그 수열의 부분 수열 중 바이토닉 수열이면서 가장 긴 수열의 길이를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 부분 수열 중에서 가장 긴 바이토닉 수열의 길이를 출력한다.

예제 입력 1 

10
1 5 2 1 4 3 4 5 2 1

예제 출력 1 

7

힌트

예제의 경우 {1 5 2 1 4 3 4 5 2 1}이 가장 긴 바이토닉 부분 수열이다.



[ 문제 풀이 ]


왼쪽방향으로부터 오른쪽으로 이동하는, 가장 긴 증가하는 부분 수열을 구한다. (D[i])

오른쪽방향으로부터 왼쪽으로 이동하는, 가장 긴 증가하는 부분 수열을 구한다. (RD[i])


D[i] + RD[i] - 1이 가장 큰 길이가 가장 긴 바이토닉 부분 수열의 길이이다.

(1을 빼는 이유는 중앙에 있는 값이 겹치기 때문이다.)


※ 주의 : D, RD 각각의 최대를 구해서 더하는게 아니다.


[ 최종 구현(C++) ]

#include <stdio.h>
#define MAX_NUMBER 1002
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int D[MAX_NUMBER] = { 0, }; // 왼쪽에서부터 증가하는 수열
int RD[MAX_NUMBER] = { 0, }; // 오른쪽에서부터 증가하는 수열
int A[MAX_NUMBER] = { 0, };

int main(void)
{
	int N;
	int result = 0;
	scanf("%d", &N);

	for (register int i = 1; i <= N; i++) scanf("%d", &A[i]);

	// 왼쪽에서부터 증가하는 수열 구하기
	D[1] = 1;
	for (register int i = 2; i <= N; i++)
	{
		D[i] = 1;

		for (register int j = 1; j <= i - 1; j++)
		{
			if (A[j] < A[i])
				D[i] = MAX(D[i], D[j] + 1);
		}
	}

	// 오른쪽에서부터 증가하는 수열 구하기
	RD[N] = 1;
	for (register int i = N-1; i >= 1; i--)
	{
		RD[i] = 1;

		for (register int j = N; j >= i + 1; j--)
		{
			if (A[j] < A[i])
				RD[i] = MAX(RD[i], RD[j] + 1);
		}
	}

	// 가장 긴 바이토닉 수열의 길이
	// = 가장 긴 증가수열 + 가장 긴 감소수열 - 1(중앙값이 겹치므로 뺀다)
	for (register int i = 1; i <= N; i++)
	{
		result = MAX(result, D[i] + RD[i] - 1);
	}

	printf("%d\n", result);

	return 0;
}