문제

수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수열은 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 이고, 합은 113이다.

입력

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

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

출력

첫째 줄에 수열 A의 합이 가장 큰 증가 부분 수열의 합을 출력한다.

예제 입력 1

1
2
10
1 100 2 50 60 3 5 6 7 8

예제 출력 1

1
113

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const [N, input] = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const A = input.split(" ").map(Number);

function maxIncreasingSubArray(A) {
const n = A.length;
const DP = [...A];

for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (A[i] > A[j] && DP[i] < DP[j] + A[i]) {
DP[i] = DP[j] + A[i];
}
}
}
return Math.max(...DP);
}

console.log(maxIncreasingSubArray(A));

풀이

  1. DP는 해당 인덱스까지 가장 긴 부분수열의 합을 나타낸다. 이를 계산하기 편하게 하기 위해 DP에 A 수열의 값을 spread 문법으로 복사해준다.

  2. 어짜피 DP[0]은 1로 확정이므로 i(인덱스)가 1부터 반복문을 순회한다.
    이는 DP에 값을 저장하기 위한 반복문이다.

  3. i 이전까지 수열 값과 A[i]값을 비교하여 A[i]가 A[j]보다 크고 DP[i] 값이 DP[j]+A[i] 보다 작으면 증가하는 부분 수열에 포함될 수 있다.

그렇기 때문에 해당 DP[i]값을 새로운 값으로 갱신해준다.

예시

1
const A = [1, 100, 2, 50, 60, 3, 5, 6, 7, 8];

1. i = 1 && j = 0 일 때

  • A[1] = 100, A[0] = 1이므로 A[i] > A[j] 조건 만족한다
  • DP[1] < DP[0] + A[1] 만족하므로 DP[1] = DP[0] + A[1]로 갱신
1
DP = [1, 101, 2, 50, 60, 3, 5, 6, 7, 8];

2. i = 2 이고

2.1. j = 0 일 때

  • A[2]=2, A[0] = 1 이므로 A[i] > A[j] 조건 만족한다.
  • DP[2] < DP[0] + A[2] 만족하므로 DP[2] = DP[0] + A[2]로 갱신

2.2. j = 1 일 때,

  • A[2]=2, A[1] = 100 이므로 A[i] > A[j] 조건 만족하지 못한다.
1
DP = [1, 101, 3, 50, 60, 3, 5, 6, 7, 8];

즉, 0번째부터 해당 인덱스까지 수열의 두 수를 비교하고 DP에 저장된 값도 비교하여 둘다 만족하면 DP 값을 갱신하는 것이다.