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

문제

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


풀이

코드

n = int(input())
nums = list(map(int, input().split()))
dp = [[1,1] for _ in range(n)]

for i in range(n):
    for j in range(i):
        if nums[i] > nums[j] :
            dp[i][0] = max(dp[i][0], dp[j][0]+1)
        elif nums[i] < nums[j] :
            dp[i][1] = max(dp[i][1], dp[j][0]+1, dp[j][1]+1)

result = 1
for i in range(n):
    v1, v2 = dp[i]
    result = max(result, v1, v2)
    
print(result)

설명

이 문제를 보고 어떤 수까지 증가를 해야 하고, 어떤 수부터 감소를 하는 것이 좋을지 결정하는 게 어려웠다. 그러다가 점화식을 찾아냈다. 부분 수열과 비슷한 로직이기는 한데, 일단 시간복잡도는 O(N^2)이 된다. dp는 2차원 배열인데 각 원소는 [증가하고 있는 수열 개수, 증가하다가 감소하고 있는 수열 길이]로 이루어진다.

i번째에서 0~i-1까지 순회하면서 nums[i] 보다 작은 nums[j]를 찾았을 때는 증가하고 있는 수열과 관련되므로 현재 값과 dp[j][0]에 1을 더한 값의 max를 넣는다. 큰 nums[j]를 찾았을 때는 이제 감소를 하는 구간으로 결정할 수 있다.

단, nums[j]가 감소를 하고 있는 것일 수도 있고 증가의 막바지라고 생각할 수 있으므로 3가지의 값을 비교해준다. 현재의 dp[i][1]과 큰 원소의 [0], [1]에 각각 1을 더한 값의 max가 dp[i][1]이 된다.

그후 n번 순회하면서 각 원소에 있는 값들 중 최댓값을 찾아서 프린트한다!