백준 c++/(1-1)백준 c++ 알고리즘 기초

백준 11054 c++ 가장 긴 바이토닉 부분 수열

현구구 2022. 7. 27. 14:07

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

 

11054번: 가장 긴 바이토닉 부분 수열

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

www.acmicpc.net


문제

수열 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의 부분 수열 중에서 가장 긴 바이토닉 수열의 길이를 출력한다.


틀린코드 - 시간초과

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main()
{
	int N;
	cin >> N;
	vector < vector <int> > dp(N+1, vector <int>(N+1, 1));//2중배열
    //dp[a][b]는 증가하다 a에서 꺽여서 감소하며 마지막 수가 b인 수열의 최대값
	vector<int> arr(N + 1, 1);
    //입력
	for (int i = 1; i <= N; i++)
	{
		cin >> arr[i];
	}

	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			for (int k = j - 1; k >= 1; k--)//i를 기준으로 이전은 증가 수열
			{
				if (arr[j] > arr[k])
				{
					dp[i][j] = max(dp[i][j], dp[i][k] + 1);
				}
			}
		}
		for (int j = i + 1; j <= N; j++)
		{
			for (int k = j - 1; k >= 1; k--)//i를 기준으로 이후는 감소 수열
			{
				if (arr[j] < arr[k])
				{
					dp[i][j] = max(dp[i][j], dp[i][k] + 1);
				}
			}
		}
	}
	int result = 0;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= N; j++)
		{
			result = max(dp[i][j],result);
		}
	}
	cout << result;
}

처음 풀 때는

index 1 2 3 4 5 6
arr 1 2 3 2 1 4

에서 index = 1에서 꺽이는 경우, index = 2 에서 꺽이는 경우.... 등등 모든 바이토닉 수열을 계산해서 풀었고 당연히 시간초과에 걸렸다.


정답코드

 

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main()
{
	int N;
	cin >> N;
	vector<int> dpP(N + 1, 1);//증가하는 수열의 최대값
	vector<int> dpM(N + 1, 1);//감소하는 수열의 최대값
	vector<int> arr(N + 1, 1);
    //입력
	for (int i = 1; i <= N; i++)
	{
		cin >> arr[i];
	}
	//증가
	for (int i = 2; i <= N; i++)
	{
		for (int j = i - 1; j > 0; j--)
		{
			if (arr[j] < arr[i])
			{
				dpP[i] = max(dpP[i], dpP[j] + 1);
			}
		}
	}
    //감소
	for (int i = N-1; i >=1; i--)
	{
		for (int j = i + 1; j <=N; j++)
		{
			if (arr[j] < arr[i])
			{
				dpM[i] = max(dpM[i], dpM[j] + 1);
			}
		}
	}
	int result = 0;
	for (int i = 1; i <= N; i++)
	{
		result = max(result, dpP[i] + dpM[i] - 1);
	}
	cout << result;
}

index 1 2 3 4 5 6
arr 1 2 3 2 1 4
dpP증가수열 1 2 3 2 1 4
dpM감소수열 1 2 3 2 1 1

이 때 증가수열 + 감소수열의 최대값이 index = 3 일 경우이다.

index = 3 일 때 dpP = {1, 2, 3}이고 dpM = {3, 2, 1}로 각각 3이다.

둘을 합치면 {1, 2, 3, 3, 2, 1}인데 이 때 3이 중복되므로 -1을 해준 값이 정답이다.