백준 11054번: 가장 긴 바이토닉 부분 수열 - javascript

3 분 소요

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

접근

이 문제는 11053번 가장 긴 증가하는 부분 수열의 양면 버전이다. 11053번에 비해 이번 문제는 최대로 증가하면서 최대로 감소하는 긴 부분 수열을 구해야한다.

그렇다면 최대로 증가하는 부분 수열과 최대로 감소하는 부분 수열간의 최대 교차부분을 값으로 산출하면 되는 것이다.

증가와 감소 두개의 수치를 측정하기 위해 dp배열을 2중으로 생성한다.

원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 - - - - - - - - - -
dp[1] - - - - - - - - - - 1 0

i = 0일 때, dp[0]은 앞에서 부터, 동시에 dp[1]은 n-i 부터 내려간다.

원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 2 - - - - - - - - -
dp[1] - - - - - - - - - 2 1 0
원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 2 2 - - - - - - - -
dp[1] - - - - - - - - 3 2 1 0
원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 2 2 1 - - - - - - -
dp[1] - - - - - - - 3 3 2 1 0
원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 2 2 1 3 - - - - - -
dp[1] - - - - - - 3 3 3 2 1 0

이렇게 진행하다 보면 최종적으로 아래와 같은 표가 나오게 된다.

원본 배열 0 1 5 2 1 4 3 4 5 2 1 0
dp[0] 0 1 2 2 1 3 3 4 5 2 1 -
dp[1] - 1 5 2 1 4 3 3 3 2 1 0

이렇게 나온 dp 배열에 관하여 각 dp[0][i] + dp[1][i] 값끼리의 max값을 구하면 5+3으로 8이 나오게 된다.
이 8이라는 숫자는 앞에서부터 5까지, 뒤에서부터 5까지의 각각의 최대 부분 증가 수열의 길이이므로 중복되는 5에 관해 1을 빼주면 8-1로 정답 7이 나오게 된다.

ps
이번 문제를 풀면서 처음에 dp 배열을 초기화 해주기 위해서

let dp = new Array(parseInt(n)+2).fill([0,0])

의 형식을 썼었는데 정답이 무조건 n으로 나왔었다.
왜 그런가 생각해보니 ‘초기화’ 해줄 때 fill에 [0, 0]의 객체를 넣어줘버렸던 것이다!..
데이터 타입과 변수에서도 공부했었는데, 자바스크립트에서 객체는 참조에 의한 전달 방식을 가진다.
그러니 당연하게도 dp[0]의 0과 dp[n]의 0은 메모리상에서 같은 주소를 참조하니, i가 0부터 n까지 향하게 되면 전부 같은 dp값을 공유하게되서 n을 결과로 반환 할 수 밖에 없었던 것이다… :joy:
따라서 그냥 평범하게 반복문을 돌려가며 dp에 [0, 0]을 push해주는 방법을 사용하는 것으로 해결했다.

정답 코드

const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
let input = [];
rl.on('line', function (line) {
  input.push(line)
})
  .on('close', async function () {
  // 답안 작성
  let answer = '';
  let n = input[0];
  let arr = '0 '+ input[1] +' 0'
  arr = arr.split(' ')
  answer = bytonic_array(n,arr)
  console.log(answer)
  process.exit();
});

function bytonic_array(n,arr){
  n = parseInt(n)
  let dp = [];
  let result = [];
  for(let i = 0; i<n+2;i++){
    dp.push([0,0])
  }
  //new Array(parseInt(n)+2).fill([0,0])
  for(let i=1;i<=n;i++){
    let max = 0;
    let max_alt = 0;    
    for(let j=0;j<i;j++){
      if(arr[j]*1 < arr[i]*1){        
        max = Math.max(dp[j][0], max);
      }      
      if(arr[n*1+1-j]*1 < arr[n*1+1-i]*1){
        max_alt = Math.max(dp[n*1+1-j][1], max_alt);
      }
    }
    dp[i][0] = max+1;    
    dp[n*1+1-i][1] = max_alt+1;
  }
  dp.reduce((acc,cur)=>{
    result.push(cur[0]+cur[1])
  },0)
  return Math.max(...result)-1;
}

댓글남기기