전공/알고리즘

백준 11054(가장 긴 바이오닉 부분 수열)

xkdlaldfjtnl 2020. 7. 17. 15:08

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

 

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

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

www.acmicpc.net

감소하다가 증가하지 않는 수열이 바이토닉 수열이라고 한다. 

 

그러니깐 증가만 해도 되고, 

감소만 해도 되고, 

증가하다가 감소해도 되고 

 

이 문제는 

어떤 수열이 주어졌을 그 수열의 부분수열 중 가장 긴 바이토닉 수열을 구하는 것이다. 

 

보자마자 생각나는 문제가 있다. LIS다 그런데 감소해야하는 경우도 구해야 한다. 

 

바이토닉 수열

dp[i] = max( i까지의 증가수열 + i부터 감소수열)

 

따라서 i까지의 증가수열과 i부터 감소수열 중 최대값을 구하자 

 

i까지의 증가수열 최댓값은 그냥 LIS다 

 

dp[i]는 뒤꽁무니 쫓아오는 값 중 그 값이 작은 값 중 dp의 최댓값 +1로 구할 수 있다. 

 

그러면 i부터 감소수열은? 

 

말장난이다. 증가나 감소나 똑같다. 선형이고 값만 살짝 바꿔주면 된다. 

 

이때 그냥 수열을 뒤집어서 LIS구하면 된다. 

 

그 뒤에 바이토닉 수열을 구하는 위 공식을 이용해서 답을 구할 수 있다. 

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 1001

int N, res=-1;
int seq[MAX];
int inc[MAX];
int decr[MAX];

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cin >> N;
	for (int i = 0; i < N; i++) {
		cin >> seq[i];
	}
	for (int i = 0; i < N; i++) {
		inc[i] = 1;
		decr[i] = 1;
	}

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < i; j++) {
			if (seq[i] > seq[j]) {
				inc[i] = max(inc[i], inc[j] + 1);
			}
		}
	}
	for (int i = N - 1; i >= 0; i--) {
		for (int j = N - 1; j > i; j--) {
			if (seq[i] > seq[j]) {
				decr[i] = max(decr[i], decr[j] + 1);
			}
		}
	}
	for (int i = 0; i < N; i++) {
		res = max(res, inc[i] + decr[i]);
	}
	cout << res - 1 << "\n";
}