문제

45656이란 수를 보자.

이 수는 인접한 모든 자리의 차이가 1이다. 이런 수를 계단 수라고 한다.

N이 주어질 때, 길이가 N인 계단 수가 총 몇 개 있는지 구해보자. 0으로 시작하는 수는 계단수가 아니다.

입력

첫째 줄에 N이 주어진다. N은 1보다 크거나 같고, 100보다 작거나 같은 자연수이다.

출력

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

예제

예제 입력 1

1
2
1

예제 출력 1

1
2
9

예제 입력 2

1
2
2

예제 출력 2

1
17

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const input = +require("fs").readFileSync("/dev/stdin").toString().trim();

const DP = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

while (DP.length !== input + 1) {
DP.push([...Array(10)]);
}

for (let i = 2; i <= input; i++) {
for (let j = 0; j <= 9; j++) {
DP[i][j] = ((DP[i - 1][j - 1] || 0) + (DP[i - 1][j + 1] || 0)) % 1000000000;
}
}

console.log(DP[input].reduce((acc, cur) => (acc + cur) % 1000000000, 0));
  • DP[i][j]의 값은 길이가 i인 수가 j(0~9)로 끝나는 모든 경우의 수이다.
  • DP 배열을 하드코딩으로 직접 만들어줬다. 왜나하면 초기값 같은 경우는 직접 만드는 편이 더 로직이 간편하기 때문이다. 이후 input의 길이보다 1 더 큰 길이로 DP 배열에 추가하였다.
    • 이렇게 하지 않으면 DP[input]의 값이 존재하지 않는 값이여서 값을 할당할 수가 없다.
  • 주어진 테케에서 길이가 1인경우, 길이가 2인 경우의 패턴을 파악하였다.
    • 예를 들어 DP[2][1]인 경우 길이가 2이고 1로 끝나는 경우의 수는 1 앞에 올 수 있는 수는 0,2이다. 이 값이 바로 DP[1][0]DP[1][2] 이다.
  • 저번 문제에서도 그렇고 왜 10억을 값을 저장할 때 한번, 총합을 구할 때 두번 나눠주는지 이해가 안된다.
  • 주의할 점은 끝나는 수가 0인 경우는 이전 숫자로 1만 올 수 있고, 끝나는 숫자가 9인 경우는 이전 숫자로 8만 올 수 있는 것을 고려해준 것이 바로 || 논리 연산자 부분이다.

댓글 공유

문제

0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 중 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다.

  1. 이친수는 0으로 시작하지 않는다.
  2. 이친수에서는 1이 두 번 연속으로 나타나지 않는다. 즉, 11을 부분 문자열로 갖지 않는다.

예를 들면 1, 10, 100, 101, 1000, 1001 등이 이친수가 된다. 하지만 0010101이나 101101은 각각 1, 2번 규칙에 위배되므로 이친수가 아니다.

N(1 ≤ N ≤ 90)이 주어졌을 때, N자리 이친수의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다.

출력

첫째 줄에 N자리 이친수의 개수를 출력한다.

예제

예제 입력 1

1
2
3

예제 출력 1

1
2

내 코드

1
2
3
4
5
6
7
8
9
const input = +require("fs").readFileSync("/dev/stdin").toString().trim();

const DP = [BigInt(0), BigInt(1)];

for (let i = 2; i <= input; i++) {
DP[i] = DP[i - 2] + DP[i - 1];
}

console.log(DP[input] + "");
  • 처음에는 길이가 i인 수가 0,1로 끝나는 수를 나타내기 위해 DP[i][0], DP[i][1]로 배열을 구성하였는데, 점화식을 알아내고 나니 DP[i] = DP[i-2] + DP[i-1]로 계산하기 위해 DP을 1차원 배열로 바꿨다.
  • N이 90까지 가능하므로 이 경우 수가 매우 커져 number의 범위가 넘어가는 경우가 있을 수 있기 때문에 BigInt()로 값을 계산해줘야 한다.

댓글 공유

문제

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

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

입력

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

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

출력

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

예제

예제 입력 1

1
2
3
6
10 20 10 30 20 50

예제 출력 1

1
4

내 코드

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

const DP = Array.from({ length: N }, () => 1);

for (let i = 1; i < N; i++) {
for (let j = 0; j < i; j++) {
if (A[j] < A[i]) {
DP[i] = Math.max(DP[i], DP[j] + 1);
}
}
}

console.log(Math.max(...DP));
  • 처음 모두 자신의 길이를 자신을 포함하므로 1로 설정해준다.
  • 만약 i 번째 수가 i보다 작은 번째의 수보다 크다면 DP[i]는 이전 DP값에 1을 더해준 것과 기존 DP[i]값 중 큰 값을 저장해둔다.
  • ex) A = [10,20,10,30,20,50]이고 DP[1]을 계산하면, A[0] < A[1] 이므로 DP[1] = DP[0] + 1이다.
    • 왜냐하면 DP는 자신의 길이를 나타내는 것인데 이전 수보다 크다는 것은 자기 자신을 수열로 포함시킬 수 있다는 뜻이므로 +1을 해주는 것이다. 또한, 자신 보다 작은 수의 DP가 여러개 일 수도 있으므로 반복문을 돌면서 이들 중 최댓값만 저장해주면 된다.

댓글 공유

문제

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

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

입력

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

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

출력

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

둘째 줄에는 가장 긴 증가하는 부분 수열을 출력한다. 그러한 수열이 여러가지인 경우 아무거나 출력한다.

예제

예제 입력 1

1
2
3
6
10 20 10 30 20 50

예제 출력 1

1
2
4
10 20 30 50

내 코드

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

const DP = Array.from({ length: N }, () => 1);

for (let i = 1; i < N; i++) {
for (let j = 0; j < i; j++) {
if (A[j] < A[i]) {
DP[i] = Math.max(DP[i], DP[j] + 1);
}
}
}

const LIS = Math.max(...DP);

let LIS_Array = [];
for (let i = 0; i < N; i++) {
const index = DP.findIndex((elem) => elem === i + 1);
LIS_Array.push(A[index]);
}

console.log(LIS);
console.log(LIS_Array.join(" "));
  • 기존 LIS를 구하는 로직에서 DP에 해당하는 LIS뿐만 아니라 그 때의 배열까지 생성해주도록 하였다.
  • 처음 배열 길이만큼 반복문을 순회하면서 DP값에서 Index를 찾는다. 해당 index의 A 수열의 값을 넣어준다.
  • 왜 틀렸는지 이유를 모르겠어서 해설을 보게되었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const [N, input] = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const A = input.split(" ").map(Number);

const DP = Array.from({ length: N }, () => 1);

for (let i = 1; i < N; i++) {
for (let j = 0; j < i; j++) {
if (A[j] < A[i]) {
DP[i] = Math.max(DP[i], DP[j] + 1);
}
}
}

let LIS = Math.max(...DP);
let LIS_Array = [];

for (let i = N - 1; i >= 0; i--) {
if (DP[i] === LIS) {
LIS_Array = [A[i], ...LIS_Array];
LIS--;
}
}

console.log(LIS_Array.length);
console.log(LIS_Array.join(" "));
  • 이전 내 코드는 findIndex 메서드로 제일 앞에서 부터 해당 인덱스를 찾아주도록 하였다. 하지만 이런 경우 반례가 생긴다. 왜냐하면 내가 처음에 DP의 초기값을 모두 1로 설정해두었기 때문에 LIS_Array의 배열의 데이터가 이상하게 들어올 수 있다

반례

N = 14, A = [10, 12, 1, 3, 5, 6, 7, 10, 12, 2, 4, 7, 10, 12]

  • findIndex로 앞에서부터 찾았을 경우: LIS_Array = [10, 12, 5, 6, 7, 10, 12]
  • 반복문 역순으로 찾았을 경우: LIS_Array = [1, 3, 5, 6, 7, 10, 12]

댓글 공유

문제

n개의 정수로 이루어진 임의의 수열이 주어진다. 우리는 이 중 연속된 몇 개의 수를 선택해서 구할 수 있는 합 중 가장 큰 합을 구하려고 한다. 단, 수는 한 개 이상 선택해야 한다.

예를 들어서 10, -4, 3, 1, 5, 6, -35, 12, 21, -1 이라는 수열이 주어졌다고 하자. 여기서 정답은 12+21인 33이 정답이 된다.

입력

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

출력

첫째 줄에 답을 출력한다.

예제

예제 입력 1

1
2
3
10
10 -4 3 1 5 6 -35 12 21 -1

예제 출력 1

1
2
33

예제 입력 2

1
2
3
10
2 1 -4 3 4 -4 6 5 -5 1

예제 출력 2

1
2
14

예제 입력 3

1
2
3
5
-1 -2 -3 -4 -5

예제 출력 3

1
-1

내 코드

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

let DP = Array.from({ length: n }, () => 0);
if (A[0] < 0) {
DP = Array.from({ length: n }, () => A[0]);
}

for (let i = 1; i < n; i++) {
for (let j = i + 1; j < n; j++) {
DP[i] = Math.max(DP[i], DP[i - 1] + A[i]);
}
}

console.log(Math.max(...DP));
  • 처음엔 연속되었다고 하여서 2연속만 생각했었다. 하지만 2번째 예제에서는 연속된 숫자가 3, 4, -4, 6, 5 이렇게 구하여 총합이 14이다.
  • 0번째부터 i번째 까지 합을 저장하여 이전값과 비교하여 최댓값을 저장해준다.
  • 즉, 01, 02, 03, … 09번째의 합중 최대값만 DP[0]에 저장해준다. 이렇게 0번째부터 9번째 DP를 구하였다.
  • 그리고 첫번째가 음수의 경우는 DP를 초기값으로 모두 설정해주었다.
  • 하지만 시간 초과가 떴다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const [n, input] = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const A = input.split(" ").map(Number);

const DP = Array.from({ length: n }, () => A[0]);

for (let i = 1; i < n; i++) {
DP[i] = Math.max(DP[i - 1] + A[i], A[i]);
}

console.log(Math.max(...DP));
  • DP는 초기 값으로 설정해준다.
  1. DP[0] = A[0]
  2. DP[1]은 DP[0] + A[1] 과 A[1] 중에 최대값을 저장한다. 왜냐하면 A[1] 이전까지의 연속된 숫자들의 합이 양수라면 A[1] 보다 크지만 음수인 경우는 A[1]부터 시작하는 것이 크기 때문이다.

댓글 공유

문제

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다. 이 경우, 수학자 숌크라테스는 “11은 3개 항의 제곱수 합으로 표현할 수 있다.”라고 말한다. 또한 11은 그보다 적은 항의 제곱수 합으로 표현할 수 없으므로, 11을 그 합으로써 표현할 수 있는 제곱수 항의 최소 개수는 3이다.

주어진 자연수 N을 이렇게 제곱수들의 합으로 표현할 때에 그 항의 최소개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 100,000)

출력

주어진 자연수를 제곱수의 합으로 나타낼 때에 그 제곱수 항의 최소 개수를 출력한다.

예제

예제 입력 1

1
2
7

예제 출력 1

1
2
4

예제 입력 2

1
2
1

예제 출력 2

1
2
1

예제 입력 3

1
2
4

예제 출력 3

1
2
1

예제 입력 4

1
2
11

예제 출력 4

1
2
3

예제 입력 5

1
2
13

예제 출력 5

1
2

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = +require("fs").readFileSync("/dev/stdin").toString().trim();

const DP = Array.from({ length: input + 1 }, (_, i) => i);

for (let i = 4; i <= input; i++) {
for (let j = 1; j <= Math.sqrt(i); j++) {
const sqrt = Math.sqrt(i);
if (sqrt === parseInt(sqrt)) {
DP[i] = 1;
} else {
DP[i] = Math.min(DP[i], DP[i - j ** 2] + 1);
}
}
}

console.log(DP[input]);
  • 처음에는 단순히 초기값을 가지고 다음값을 구하는 식으로만 했었는데, 이렇게 할 경우 최소항을 비교하여 저장하면서 진행할 수 없다고 판단하여 2중 for문을 사용하였다.
  • 그 결과, 각 DP 마다 해당 index의 제곱근을 빼준 숫자의 DP에 +1 해준 것과 현재 DP를 비교했다.

ex) input = 18이라면, DP[18] = 18인 상태로 시작한다.

  1. j = 1 인 경우, DP[17] + 1 = 3이므로, DP[18] = 3를 저장한다.
  2. j = 2 인 경우, DP[14] + 1 = 4이므로, 이전 값을 그대로 둔다.
  3. j = 3 인 경우, DP[9] + 1 = 2 이므로, DP[18] = 2를 저장한다.
  4. j = 4 인 경우, DP[2] + 1 = 3 이므로 이전 값을 그대로 둔다.

j는 제곱근 이전 까지만 순회를 하므로 순회를 종료하고 DP값을 출력한다.

따라서, DP[18] = 2 로 (9+9) 이렇게 답을 구할 수 있다.

댓글 공유

문제

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

예제 입력 1

1
2
10
1 5 2 1 4 3 4 5 2 1

예제 출력 1

1
7

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const [N, input] = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const A = input.split(" ").map(Number);

function LBS(arr) {
const N = arr.length;
const DP_inc = Array.from({ length: N }, () => 1);
const DP_dec = Array.from({ length: N }, () => 1);

for (let i = 0; i < N; i++) {
for (let j = 0; j < i; j++) {
if (A[j] < A[i]) {
DP_inc[i] = Math.max(DP_inc[i], DP_inc[j] + 1);
}
}
}

for (let i = N - 1; i >= 0; i--) {
for (let j = i + 1; j < N; j++) {
if (A[j] < A[i]) {
DP_dec[i] = Math.max(DP_dec[i], DP_dec[j] + 1);
}
}
}

const DP = [];
for (let i = 0; i < N; i++) {
DP.push(DP_inc[i] + DP_dec[i] - 1);
}

return Math.max(...DP);
}

console.log(LBS(A));

해설

  1. 수열의 시작 부분부터 해당 index의 증가하는 부분수열 길이를 구한다.
  2. 수열의 마지막 부분부터 해당 index의 감소하는 부분수열의 길이를 구한다.
  3. 증가하는 부분 수열 길이와 감소하는 부분 수열의 길이를 더하여 가장 큰 값의 길이를 구하면 된다.

단, 이 때 기본값으로 세팅한 길이 1이 2번 더해진 것이므로 1을 빼줘야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 나의 오답
function LBS(arr) {
const N = arr.length;
const DP = Array.from({ length: N }, () => 1);

for (let i = 1; i < N; i++) {
for (let j = 0; j < i; j++) {
if (A[j] < A[i]) {
DP[i] = Math.max(DP[i], DP[j] + 1);
}
}
for (let k = i + 1; k < N; k++) {
if (A[k] >= A[k - 1]) break;

DP[i] = Math.max(DP[i], DP[i] + 1);
}
}

return Math.max(...DP.map((elem) => elem - 1));
}

console.log(LBS(A));

처음에는 감소하는 부분 수열을 구할 때, 역순으로 시작하지 않고 증가하는 부분 수열 구할 때 새로운 반복문으로 해당 index부터 시작하는 감소하는 부분 수열을 구하였는데 이는 DP라는 배열 하나를 가지고 반복문을 돌렸기 때문에 제대로 동작하지 않고 저장값이 꼬여버릴 수 있다.

댓글 공유

문제

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

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

입력

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

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

출력

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

예제 입력 1

1
2
6
10 30 10 20 20 10

예제 출력 1

1
3

내 코드

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 LDS(arr) {
const N = arr.length;
const DP = Array.from({ length: N }, () => 1);

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

console.log(LDS(A));

해설

지난 번에 가장 긴 증가하는 부분수열과 조건문만 수정하면 되는 문제였다.

DP는 A 수열의 index에서 감소하는 부분 수열의 길이를 저장한다.

DP의 해당 index와 이전 index까지의 수열 요소와 비교하여 이전 index가 더 작다면, DP값을 갱신해주는 문제이다.

댓글 공유

문제

수열 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 값을 갱신하는 것이다.

댓글 공유

문제

0부터 N까지의 정수 K개를 더해서 그 합이 N이 되는 경우의 수를 구하는 프로그램을 작성하시오.

덧셈의 순서가 바뀐 경우는 다른 경우로 센다(1+2와 2+1은 서로 다른 경우). 또한 한 개의 수를 여러 번 쓸 수도 있다.

입력

첫째 줄에 두 정수 N(1 ≤ N ≤ 200), K(1 ≤ K ≤ 200)가 주어진다.

출력

첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.

예제

예제 입력 1

1
2
20 2

예제 출력 1

1
2
21

예제 입력 2

1
2
6 4

예제 출력 2

1
84

내 코드

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

const DP = Array.from({ length: N + 1 }, (_, i) => {
if (i === 1) return Array.from({ length: K + 1 }, (_, i) => i + 1);
return new Array(K + 1).fill(1);
});

for (let i = 2; i <= N; i++) {
for (let j = 1; j <= K; j++) {
DP[i][j] = (DP[i - 1][j] + DP[i][j - 1]) % 1000000000;
}
}

console.log(DP[N][K - 1]);
  • ✏️ N = 1인 경우 K = 1,2,3 …200 경우의 수를 생각해보았다.
    • ❗️ 1개의 수로 1을 만들 수 있는 경우의 수는 1가지이다.
    • 2개의 수로 1을 만들 수 있는 경우의 수는 0+1, 1+0으로 2가지이다.
    • 3개의 수로 1을 만들 수 있는 경우의 수는 0+0+1, 0+1+0, 1+0+0으로 3가지이다…
  • N = 2인 경우 K = 1,2,3 …200 경우의 수를 생각해보았다.
    • ❗️ 1개의 수로 2를 만들 수 있는 경우의 수는 1가지이다.
    • 2개의 수로 2를 만들 수 있는 경우의 수는 1+1, 2+0, 0+2로 3가지이다.
    • 3개의 수로 2를 만들 수 있는 경우의 수는 1+1+0, 1+0+1, 0+1+1, 2+0+0, 0+2+0, 0+0+2로 6가지이다…

❗️의 조건을 가지고 DP[N][k] 2차원 배열에서, DP[N][1] = 1인 것을 알 수 있다.

✏️의 조건을 가지고 DP[1][k] = K 인 것을 알 수 있다.

Frame.png

위와 같은 점화식을 발견하였고 그에 알맞은 식을 만들었다.

댓글 공유

loco9939

author.bio


author.job