백준 11053번: 가장 긴 증가하는 부분 수열 - javascript

2 분 소요

백준 11053번: 가장 긴 증가하는 부분 수열

문제 설명

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

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

출력

첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다.

접근

0부터 N번째까지의 부분 증가 수열의 최대길이를 구하는 문제다. 예시로 나온 배열로 생각해보자.

i = 0 일때

배열 값 10 20 10 30 20 50
최대길이 1 0 0 0 0 0

0번째 값보다 작은수는 없으므로 최대 길이는 1이다

i = 1 일때

배열 10 20 10 30 20 50
최대길이 1 2 0 0 0 0

1번쨰까지 배열 중 1번째 값보다 작은 수는 10하나가 있는데 10까지의 최대 부분증가 수열의 길이는 1이므로 1 + 1 = 2가된다.

i = 2 일때

배열 10 20 10 30 20 50
최대길이 1 2 1 0 0 0

2번째까지 배열 중 2번째 값보다 작은 수는 없으므로 i = 2의 최대 부분 증가 수열의 길이는 1이다.

i = 3 일때

배열 10 20 10 30 20 50
최대길이 1 2 1 3 0 0

3번째까지 배열 중 3번째 값인 30보다 작은 수는 10, 20, 10 전부 해당된다. 이 중에서 20까지의 부분증가 수열 길이인 2가 제일 크기때문에 2 + 1 = 3으로 30까지의 최대 부분 증가 수열의 길이는 3이 된다.

결론

배열 10 20 10 30 20 50
최대길이 1 2 1 3 2 4

위와 같은 방식으로 진행하다보면 20은 최대 2, 마지막 수인 50은 앞서나온 수열 중 최대 길이인 3과 더해서 길이가 4인 부분 증가 수열이 된다.

이렇게 해서 구해진 최대길의 배열의 max 값을 구하면 된다.

++문제를 풀다가 이런 질문을 봤다. 피보나치 문제처럼 dp(i)에 dp(i-1)이나 dp(i-2)가 관여하지 않는데 왜 DP문제인가요? 나도 이런 생각을 했는데, dp의 핵심은 하나의 문제를 여러 하위 문제로 나누어 풀고, 그 과정 중에서 중복적인 계산을 줄이기 위해 하위 계산의 결과를 저장하는 메모제이션이라고 생각한다.

따라서 방금 문제의 경우에도 만약 우리가 최대 부분 증가 수열의 길이를 저장하는 배열을 따로 두지않았더라면 반복문의 매 i 번째 마다 0부터 i 번째까지의 부분 수열들의 길이를 비교하는 계산을 했어야 할 것이다.

하지만 메모제이션을 통해 그 결과들을 저장해두었고 우리는 i번째마다 그 이전의 부분 수열 중 최대값이 i번째 값마다 작고 제일 긴 부분 증가수열을 가져옴으로서 dp알고리즘으로 문제를 푼 것이 되는 것이다.

++javascript로 풀때 잘 봐야할 점

javascript는 변수를 선언할 때 데이터 타입을 직접 언급하지 않는다. 따라서 이번과 같은 문제를 풀때 두번째 입력값인 whitespace로 이루어진 배열을

let array = input[0].split(' ')
array.reduce((acc,cur)=>{
  let tmp = 1;
  if(cur<tmp){
    blablabla
  }
})

의 형식으로 입력받아서 사용하게 되는데, 이때 다행히 곱연산이나 for loop문으로 계산하기 되면 운좋게 넘어갈 수 도 있지만, 나처럼 reduce를 활용하여 직접 배열 값을 활용하는 경우 string형으로 인식되어있어서 오류가 날 수 도있다.

심지어 예제는 전부 10의 배수여서 string으로 이루어진 숫자도 유니코드 순으로 비교하는 javascript에서는 위처럼 cur과 tmp를 비교하게되면 javascript는 1이 10보다는 크고 20보다는 작다고 결과를 도출 할 것이다.

코드 잘 짜놓고도 틀릴 수 있는 문제점이다. 항상 숫자 연산 비교가 필요하면 비교변수를 *1이나 parseInt()를 이용해서 숫자로 바꿔주자.

정답 코드

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 arr = input[1].split(' ');
  let answer = max_sub_dp(arr);
  console.log(answer);
  process.exit();
});

function max_sub_dp(arr){
  let max_sub = arr.reduce((acc,cur,idx,arry)=>{    
    let tmp = [];
    for(let i = 0 ; i<idx+1;i++){      
      if(arry[i]<parseInt(cur)){
        tmp.push(acc[i])
      }
    }    
    if(tmp.length>0){
      acc[idx] += Math.max(...tmp);
    }    
    return acc;
  },Array(arr.length).fill(1));    
  return Math.max(...max_sub);
}

댓글남기기