Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 11054 - 가장 긴 증가하는 바이토닉 부분 수열

sm_amoled 2021. 12. 25. 13:15

문제링크

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

조건

  • 시간 제한 : 1s
  • 메모리 제한 : 256MB

해설

수 하나를 기준으로 좌우로 수를 뽑았을 때, index 순서대로 오름차순 / 내림차순을 만들 수 있는 뽑은 수열의 길이의 최댓값을 묻는 문제이다. 예를 들어, 다음과 같이 바이토닉 수열을 만들 수 있다.

  • 1 5 2 1 4 3 4 5 2 1 → 바이토닉 수열의 최대 길이 : 7
  • 1 5 2 1 4 3 4 5 → 바이토닉 수열의 최대 길이 : 5 ← 오름치순 또는 내림차순이 없을 수도 있음!
  • 1 2 3 2 1 7 → 바이토닉 수열의 최대 길이 : 5 ← 최대 숫자가 기준이 아님!

주어진 수열에서 왼쪽에서 오른쪽으로 갈 때 오름차순으로 만들 수 있는 조건을 따져 처음부터 해당 index까지에서 만들 수 있는 최대 오름차순 수열의 길이를 구하고, 왼쪽에서 오른쪽으로 갈 때 오름차순으로 만들 수 있는 조건을 따져 끝부터 해당 index까지에서 만들 수 있는 최대 오름차순 수열의 길이를 구하여 합치면 쉽게 해결할 수 있다.

풀이

아래 수열을 예시로 하여 해설에서의 해결 방법을 적용해보자.

input : 1 5 2 1 4 3 4 5 2 1

incre : 1 2 2 1 3 3 4 5 2 1
decre : 1 5 2 1 4 3 3 3 2 1
lengt : 1 6 3 1 6 5 6 7 3 1

좌→우 방향으로 오름차순인 increase와 좌←우 방향으로 오름차순(좌→우 내림차순)인 decrease의 합에서 1을 뺀 length는 해당 index를 좌우를 나누는 기준으로 선택했을 때 만들 수 있는 바이토닉 수열의 길이이다. 1을 빼는 이유는 자신이 2번 수열에 포함되기 때문!

각 i번째 원소 / N-1-i 번째 원소가 좌→우 / 우→좌 방향으로 오름차순 수열에 포함되면 최대 몇 번째 원소가 될 수 있는지를 구하여 increase / decrease에 저장한다. 뒤에서 앞으로 오는 decrease는 역순으로 저장해야 좌→우 방향으로 읽었을 때 내림차순으로 만들 수 있으므로 index가 뒤에서부터 온다. 이를 코드로 구현하면 아래와 같다.

for(int i = 0; i < N; i++) {
    for(int j = 0; j < i; j++) {
        if(input[j] < input[i]) {
            if(increase[i] < increase[j]+1) {
                increase[i] = increase[j] + 1;
            }
        }
        if(input[N-1-j] < input[N-1-i]) {
            if(decrease[N-1-i] < decrease[N-1-j] + 1) { 
                decrease[N-1-i] = decrease[N-1-j] + 1;
            }
        }
    }
}

increase와 decrease의 같은 index간의 합 중 가장 큰 값에서 1을 뺀 값이 원하는 가장 긴 바이토닉 수열의 길이이다. 이 값을 출력하면 원하는 결과를 얻을 수 있다.

int max = 0;
for(int i = 0; i < N; i++) {
    increase[i] = increase[i]+decrease[i];
    max = (max > increase[i])? max : increase[i];
}

cout << max - 1;

코멘트

https://sihyungyou.github.io/baekjoon-11054/ 여기에서 도움을 얻었다. 어렵군... 어려워...

고민

처음에는 가장 큰 값을 잡고 좌우로 오름/내림차순을 돌리려고 했는데, 이 방법은 해결책이 아니더라. 위에 예시를 든 것 처럼 가장 큰 값이 선택의 기준이 되면 최댓값을 못갖는 경우가 생겼었다.

그 다음에는 좌/우로 LDS 알고리즘을 따로 적용해서 각 길이를 구하려고 시도했었다. 이 방법의 문제는, 어떤 원소를 좌우로 나누는 기준점으로 골라야 LDS를 적용했을 때 정확한 원소 길이를 구할 수 있는지를 알 수 없다는 것이였다.

그래서 검색에서 위 블로그의 도움을 받아 문제를 해결할 수 있었다. 고통스러웠다. ㅋㅋㅋ. 자꾸 검색을 너무 쉽게 하는 것 같은 느낌이 들기도 하는데, LDS 알고리즘의 시간 복잡도가 오래 걸리는 버전을 사용해야 한다는 것을 놓쳤던 것 같다. 나는 O(n log n) 방법밖에 모른단 말야... ㅋㅋㅋㅋ


코드

#include <iostream>
#include <vector>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    int N;
    cin >> N;

    // 입력    
    vector<int> input(N);
    int temp;
    for(int i = 0; i < N; i++) {
        cin >> temp;
        input[i] = temp;
    }
    
    // DP
    vector<int> increase(N, 1);
    vector<int> decrease(N, 1);
    
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < i; j++) {
            if(input[j] < input[i]) {
                if(increase[i] < increase[j]+1) {
                    increase[i] = increase[j] + 1;
                }
            }
            if(input[N-1-j] < input[N-1-i]) {
                if(decrease[N-1-i] < decrease[N-1-j] + 1) { 
                    decrease[N-1-i] = decrease[N-1-j] + 1;
                }
            }
        }
    }
    
    int max = 0;
    for(int i = 0; i < N; i++) {
        increase[i] = increase[i]+decrease[i];
        max = (max > increase[i])? max : increase[i];
    }
    
    cout << max - 1;
    return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 1904 - 01타일  (0) 2021.12.25
[백준] 11048 - 이동하기  (0) 2021.12.25
[백준] 17298 - 오큰수  (0) 2021.12.25
[백준] 2293 - 동전 1  (0) 2021.12.25
[백준] 2304 - 창고 다각형  (0) 2021.12.25