문제

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 3가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다. 단, 같은 수를 두 번 이상 연속해서 사용하면 안 된다.

  • 1+2+1
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 100,000보다 작거나 같다.

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.

예제

예제 입력 1

1
2
3
4
5
3
4
7
10

예제 출력 1

1
2
3
3
9
27

내 코드

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
38
39
// 시간 초과
const input = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const T = +input.shift();
const nums = input.map(Number);
const N = Math.max(...nums);

// DP[n][k] 는 n을 만들 때 끝의 자리수가 k로 끝나는 경우의 수를 나타낸다. (k = 1,2,3)
const DP = Array.from({ length: N + 1 }, () => new Array(4).fill(0));

DP[1][1] = 1;
DP[1][2] = 0;
DP[1][3] = 0;
DP[2][1] = 0;
DP[2][2] = 1;
DP[2][3] = 0;
DP[3][1] = 1;
DP[3][2] = 1;
DP[3][3] = 1;

nums.forEach((num) => {
for (let i = 4; i <= num; i++) {
for (let j = 1; j <= 3; j++) {
if (j === 1) {
DP[i][j] = (DP[i - j][2] + DP[i - j][3]) % 1000000009;
}
if (j === 2) {
DP[i][j] = (DP[i - j][1] + DP[i - j][3]) % 1000000009;
}
if (j === 3) {
DP[i][j] = (DP[i - j][1] + DP[i - j][2]) % 1000000009;
}
}
}
console.log(DP[num].reduce((acc, cur) => (acc + cur) % 1000000009, 0));
});
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
const input = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
const T = +input.shift();
const nums = input.map(Number);
const N = Math.max(...nums);

// DP[n][k] 는 n을 만들 때 끝의 자리수가 k로 끝나는 경우의 수를 나타낸다. (k = 1,2,3)
const DP = Array.from({ length: N + 1 }, () => new Array(4).fill(0));

DP[1][1] = 1; // 1
DP[1][2] = 0;
DP[1][3] = 0;
DP[2][1] = 0;
DP[2][2] = 1; // 2
DP[2][3] = 0;
DP[3][1] = 1; // 2+1
DP[3][2] = 1; // 1+2
DP[3][3] = 1; // 3

for (let i = 4; i <= N; i++) {
for (let j = 1; j <= 3; j++) {
for (let k = 1; k <= 3; k++) {
if (j === k) continue;
DP[i][j] += DP[i - j][k] % 1000000009;
}
}
}

nums.forEach((num) => {
console.log(DP[num].reduce((acc, cur) => (acc + cur) % 1000000009, 0));
});
  1. DP[n][k]는 n 을 만들 때, 끝의 자리수가 k로 끝나는 경우의 수를 나타낸다. (k = 1,2,3)
  2. j가 1이면 k는 1이 아니여야만 가능하다. 왜냐하면 연속해서 올 수 없기 때문이다.
  3. DP 저장을 해줄 때에도 1000000009을 나눠주고 합한 것도 나눠줘야하는 이유를 모르겠다.

댓글 공유

문제

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값을 갱신해주는 문제이다.

댓글 공유

CSS Box model

CSS Box model은 모든 HTML 요소를 감싸는 박스이다.

  • content: 텍스트, 이미지가 나타날 박스
  • padding: 컨텐츠 주변의 투명한 공간
  • border: padding과 content 사이의 테두리
  • margin: border 바깥의 투명한 공간
1
2
3
4
5
6
div {
width: 300px;
border: 15px solid green;
padding: 50px;
margin: 20px;
}

❗️ 주의사항

box model의 width, height를 설정하면, 이것은 content 부분의 width, height를 설정한 것이다.

전체 사이즈를 계산하기 위해서는 padding, border, margin을 모두 더해줘야한다.

1
2
3
4
5
6
div {
width: 320px;
padding: 10px;
border: 5px solid gray;
margin: 0;
}

요소의 총 width = width + left padding + right padding + left border + right border + left margin + right margin

요소의 총 height = height + top padding + bottom padding + top border + bottom border + top margin + bottom margin

요소의 총 width는 350px이다.

width와 height

width와 heigth로 올 수 있는 값은 다음과 같다.

  • auto: 기본값으로, 브라우저가 width와 height를 계산한다.
  • length: px, cm 등등
  • %: containing block의 퍼센트
  • initial: 기본값으로 설정
  • inherit: 부모 값으로 부터 상속 받은 값

max와 min

max와 min은 length 값으로 설정할 수 있다.

ex) px, cm, containing block의 %

1
2
3
4
5
div {
max-width: 500px;
height: 100px;
background-color: powderblue;
}

는 브라우저 width가 500px보다 작아지면 수평으로 스크롤이 생겨난다.

스크롤을 해결하기 위해서는 max-width를 사용하면 가능하다.

1
2
3
4
5
div {
max-width: 500px;
height: 100px;
background-color: powderblue;
}

max-width와 width를 같은 요소에 사용할 때, width 속성이 max-width 속성보다 큰 경우 max-width 속성이 사용된다.

box-sizing

만약, padding 값에 상관없이 width를 설정한 값을 content 그대로 사용하고 싶다면 box-sizing 속성을 설정한다.

1
2
3
4
5
div {
width: 300px;
padding: 25px;
box-sizing: border-box;
}

위 코드는 padding 값이 어떻든 content의 width는 300px로 설정된다.

margin

margin으로 가로로 가운데 정렬

margin을 사용하면 컨테이너 내부의 요소를 수평으로 가운데에 위치하도록 설정할 수 있다.

1
2
3
4
5
div {
width: 300px;
margin: auto;
border: 1px solid red;
}

margin collpase

두개의 margin이 하나처럼 동작할 때가 있다.

1
2
3
4
5
6
7
h1 {
margin: 0 0 50px 0;
}

h2 {
margin: 20px 0 0 0;
}

h1의 bottom margin이 50px이고 h2의 top margin이 20px이다.

h1 아래에 h2가 위치해 있을 때, 예상대로라면 h1과 h2 사이에는 70px이라고 예상되지만 margin-collpase 현상으로 인해 둘 중 큰 margin만 적용된다.

이는 오직 top, bottom에서만 발생한다. left, right margin에서는 발생하지 않는다.

border

margin과 padding과 달리 border는 style과 width, color를 설정할 수 있다.

또한 원하는 방향에 각각 스타일링을 해줄 수 있다.

1
2
3
p {
border-left: 6px solid red;
}

1
2
3
p {
border-bottom: 6px solid red;
}

테두리 둥글게

1
2
3
4
p {
border: 2px solid red;
border-radius: 5px;
}

댓글 공유

loco9939

author.bio


author.job