Programming/JavaScript

[백준/JavaScript] 가장 긴 바이토닉 부분 수열(11054)

코딩뽀시래기 2024. 6. 5. 23:16
728x90

문제

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


풀이

  • 수열에서 한 수를 기준으로 왼쪽의 오름차순, 오른쪽의 내림차순 최대길이를 구해보면 된다.
  • 가운데에 끼일 수를 num번이라 하고, num-1번부터 감소하면서 더 작은 수를 체크한다.
  • 더 작은 수를 발견하면, 그 수를 수열에 포함한 게 최대길이일지, 포함하지 않은 게 최대길이일지 체크한다.
  • 반대로 num+1번부터 증가하면서도 같은 방식으로 체크한다.
// 백준 - 가장 긴 바이토닉 부분 수열(11054)
let leftDp = [];
let rightDp = [];
let list = [];

const solution = ( N ) => {
  leftDp = new Array(N).fill(-1);
  rightDp = new Array(N).fill(-1);

  for(let i = 0; i < N; i++) {
    leftUp(i);  // 왼쪽 수열 체크
    rightDown(i); // 오른쪽 수열 체크
  }
  
  let max = 0;
  for(let i = 0; i < N; i++) {
    // 1을 더하는 이유: 왼쪽, 오른쪽을 나누는 기준인 가운데 수도 포함해야 함
    max = Math.max(max, leftDp[i] + rightDp[i] + 1);
  }

  console.log(max);
};

// 왼쪽 오름차순
const leftUp = (num) => {
  // 탐색하지 않은 위치
  if(leftDp[num] == -1) {
    leftDp[num] = 0;

    for(let i = num - 1; i >= 0; i--) {
      // 왼쪽에서 본인보다 작은 수 발견
      if(list[num] > list[i]) {
        // 해당 수를 포함하는 게 더 수열이 길지 확인
        leftDp[num] = Math.max(leftDp[num], leftUp(i) + 1);
      }
    }
  }

  return leftDp[num];
}

// 오른쪽 내림차순
// 왼쪽 오름차순
const rightDown = (num) => {
  // 탐색하지 않은 위치
  if(rightDp[num] == -1) {
    rightDp[num] = 0;

    for(let i = num + 1; i < rightDp.length; i++) {
      // 오른쪽에서 본인보다 작은 수 발견
      if(list[num] > list[i]) {
        // 해당 수를 포함하는 게 더 수열이 길지 확인
        rightDp[num] = Math.max(rightDp[num], rightDown(i) + 1);
      }
    }
  }

  return rightDp[num];
}

// 테스트용
/*
const fs = require('fs');
const input = fs.readFileSync(__dirname + '/input.txt').toString().split('\n');

const N = parseInt(input[0]);
list = input[1].split(' ').map(el => parseInt(el));

solution(N);
*/

// 제출용
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

readline.on("line", (line) => {
  input.push(line);
}).on("close", () => {
  const N = parseInt(input[0]);
  list = input[1].split(' ').map(el => parseInt(el));

  solution(N);
  process.exit();
});
728x90