algorithm

알고리즘 관련 포스팅 모음

# Level 0. 직사각형 넓이 구하기

직사각형 넓이 구하기

문제 설명

2차원 좌표 평면에 변이 축과 평행한 직사각형이 있습니다. 직사각형 네 꼭짓점의 좌표 [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]가 담겨있는 배열 dots가 매개변수로 주어질 때, 직사각형의 넓이를 return 하도록 solution 함수를 완성해보세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function solution(dots) {
  let width = 0;
  let height = 0;

  for (let i = 1; i < 4; i++) {
    if (dots[i][0] === dots[0][0]) {
      height = Math.abs(dots[i][1] - dots[0][1]);
      continue;
    }

    if (dots[i][1] === dots[0][1]) {
      width = Math.abs(dots[i][0] - dots[0][0]);
    }
  }

  return width * height;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function solution(dots) {
  let width = 0;
  let height = 0;

  for (let i = 1; i < 4; i++) {
    if (dots[i][0] === dots[0][0]) {
      height = Math.abs(dots[i][1] - dots[0][1]);
      continue;
    }

    if (dots[i][1] === dots[0][1]) {
      width = Math.abs(dots[i][0] - dots[0][0]);
    }
  }

  return width * height;
}

리팩토링

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(dots) {
  const [benchmarkX, benchmarkY] = dots[0];
  let width = 0;
  let height = 0;

  dots.forEach(([x, y], index) => {
    if (index === 0) return;
    if (!height && x === benchmarkX) height = Math.abs(benchmarkY - y);
    if (!width && y === benchmarkY) width = Math.abs(benchmarkX - x);
  });

  return width * height;
}

# Level 0. 다항식 더하기

다항식 더하기

문제 설명

한 개 이상의 항의 합으로 이루어진 식을 다항식이라고 합니다. 다항식을 계산할 때는 동류항끼리 계산해 정리합니다. 덧셈으로 이루어진 다항식 polynomial이 매개변수로 주어질 때, 동류항끼리 더한 결괏값을 문자열로 return 하도록 solution 함수를 완성해보세요. 같은 식이라면 가장 짧은 수식을 return 합니다.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function solution(polynomial) {
  const answer = [0, 0];
  const splitted = polynomial.split(" + ");

  if (splitted.length === 1) return polynomial;

  splitted.forEach((term) => {
    if (term[term.length - 1] !== "x") {
      answer[1] += term * 1;
      return;
    }

    answer[0] += term === "x" ? 1 : term.slice(0, term.length - 1) * 1;
  });

  if (answer[0] === 1) answer[0] = "";
  answer[0] += "x";
  if (answer[0] === "0x") answer.shift();
  if (answer[1] === 0) answer.pop();

  return answer.join(" + ");
}

리팩토링

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
const filterPolynomial = (polynomialStr) => {
  const validTerm = new RegExp("[0-9]*x|[0-9]+", "g");
  return polynomialStr.match(validTerm);
};

const getPolynomialResult = (polynomialArr) => {
  const result = [0, 0];
  polynomialArr.forEach((term) => {
    if (term[term.length - 1] !== "x") {
      result[1] += parseInt(term);
      return;
    }

    result[0] += term === "x" ? 1 : parseInt(term);
  });

  return result;
};

const formatPolynomial = (xNumber, constant) => {
  if (!xNumber) return [constant];

  const result = [];
  xNumber === 1 ? result.push("x") : result.push(`${xNumber}x`);
  if (constant) result.push(constant);

  return result;
};

function solution(polynomial) {
  const filtered = filterPolynomial(polynomial);
  const [x, constant] = getPolynomialResult(filtered);
  const answer = formatPolynomial(x, constant);

  return answer.join(" + ");
}

두번째 리팩토링

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function solution(polynomial) {
  const answer = [0, 0];
  const splitted = polynomial.split(" + ");

  if (splitted.length === 1) return polynomial;

  splitted.forEach((term) => {
    let index = 0;
    if (term[term.length - 1] !== "x") index = 1;

    answer[index] += term === "x" ? 1 : parseInt(term);
  });

  if (!answer[0]) return `${answer[1]}`;
  if (!answer[1]) answer.pop();
  answer[0] === 1 ? (answer[0] = "x") : (answer[0] += "x");

  return answer.join(" + ");
}

# Level 0. 7의 개수

7의 개수

문제 설명

머쓱이는 행운의 숫자 7을 가장 좋아합니다. 정수 배열 array가 매개변수로 주어질 때, 7이 총 몇 개 있는지 return 하도록 solution 함수를 완성해보세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
function solution(array) {
  let answer = 0;
  array.forEach((num) => {
    while (num) {
      if (num % 10 === 7) answer++;
      num = Math.floor(num / 10);
    }
  });
  return answer;
}

# Level 0. 최댓값 만들기 (2)

최댓값 만들기 (2)

문제 설명

정수 배열 numbers가 매개변수로 주어집니다. numbers의 원소 중 두 개를 곱해 만들 수 있는 최댓값을 return하도록 solution 함수를 완성해주세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
function solution(numbers) {
  let max = -Infinity;
  for (let i = 0; i < numbers.length; i++) {
    for (let j = i + 1; j < numbers.length; j++) {
      if (max < numbers[i] * numbers[j]) max = numbers[i] * numbers[j];
    }
  }
  return max;
}

# Level 0. 잘라서 배열로 저장하기

잘라서 배열로 저장하기

문제 설명

문자열 my_str과 n이 매개변수로 주어질 때, my_str을 길이 n씩 잘라서 저장한 배열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
function solution(my_str, n) {
  const answer = [];
  let index = 0;
  while (index < my_str.length) {
    answer.push(my_str.slice(index, index + n));
    index += n;
  }
  return answer;
}

# Level 0. 캐릭터의 좌표

캐릭터의 좌표

문제 설명

머쓱이는 RPG게임을 하고 있습니다. 게임에는 up, down, left, right 방향키가 있으며 각 키를 누르면 위, 아래, 왼쪽, 오른쪽으로 한 칸씩 이동합니다. 예를 들어 [0,0]에서 up을 누른다면 캐릭터의 좌표는 [0, 1], down을 누른다면 [0, -1], left를 누른다면 [-1, 0], right를 누른다면 [1, 0]입니다. 머쓱이가 입력한 방향키의 배열 keyinput와 맵의 크기 board이 매개변수로 주어집니다. 캐릭터는 항상 [0,0]에서 시작할 때 키 입력이 모두 끝난 뒤에 캐릭터의 좌표 [x, y]를 return하도록 solution 함수를 완성해주세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function solution(keyinput, board) {
  const character = [0, 0];
  const halfWidth = (board[0] - 1) / 2;
  const halfHeigth = (board[1] - 1) / 2;
  keyinput.forEach((key) => {
    switch (key) {
      case "left":
        if (character[0] > -halfWidth) character[0]--;
        break;
      case "right":
        if (character[0] < halfWidth) character[0]++;
        break;
      case "down":
        if (character[1] > -halfHeigth) character[1]--;
        break;
      case "up":
        if (character[1] < halfHeigth) character[1]++;
        break;
    }
  });
  return character;
}

# Level 0. 문자열안에 문자열

문자열안에 문자열

문제 설명

문자열 str1, str2가 매개변수로 주어집니다. str1 안에 str2가 있다면 1을 없다면 2를 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
function solution(str1, str2) {
  const strRegExp = new RegExp(str2);
  return strRegExp.test(str1) ? 1 : 2;
}

# Level 0. 문자열 정렬하기 (2)

문자열 정렬하기 (2)

문제 설명

영어 대소문자로 이루어진 문자열 my_string이 매개변수로 주어질 때, my_string을 모두 소문자로 바꾸고 알파벳 순서대로 정렬한 문자열을 return 하도록 solution 함수를 완성해보세요.

제한사항

문제풀이

1
2
3
function solution(my_string) {
  return my_string.toLowerCase().split("").sort().join("");
}

# Level 0. OX퀴즈

OX퀴즈

문제 설명

덧셈, 뺄셈 수식들이 ‘X [연산자] Y = Z’ 형태로 들어있는 문자열 배열 quiz가 매개변수로 주어집니다. 수식이 옳다면 “O”를 틀리다면 “X”를 순서대로 담은 배열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
10
11
const getExpressionResult = (expression) => {
  const [X, operator, Y, _, Z] = expression.split(" ");
  let result = X * 1;
  result += operator === "+" ? Y * 1 : Y * -1;
  return result === Z * 1 ? "O" : "X";
};

const solution = (quiz) => {
  const answer = quiz.map((expression) => getExpressionResult(expression));
  return answer;
};

# Level 0. n의 배수 고르기

n의 배수 고르기

문제 설명

정수 n과 정수 배열 numlist가 매개변수로 주어질 때, numlist에서 n의 배수가 아닌 수들을 제거한 배열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
function solution(n, numlist) {
  return numlist.filter((num) => !(num % n));
}

두번째 문제 풀이

1
2
3
4
5
6
function solution(n, numlist) {
  return numlist.filter((num) => {
    if (num % n === 0) return true;
    return false;
  });
}

# Level 0. 숫자 찾기

숫자 찾기

문제 설명

정수 num과 k가 매개변수로 주어질 때, num을 이루는 숫자 중에 k가 있으면 num의 그 숫자가 있는 자리 수를 return하고 없으면 -1을 return 하도록 solution 함수를 완성해보세요.

제한사항

첫번째 문제 풀이

1
2
3
4
5
6
function solution(num, k) {
  const numArr = num.toString().split("");
  const strK = k.toString();
  if (numArr.includes(strK)) return numArr.indexOf(strK) + 1;
  return -1;
}

두번째 문제 풀이

1
2
3
4
5
6
7
8
9
10
11
function solution(num, k) {
  let index = 0;
  const length = num.toString().length;
  let answer = -1;
  while (num) {
    if (num % 10 === k) answer = length - index;
    num = Math.floor(num / 10);
    index++;
  }
  return answer;
}

# Level 0. 세균 증식

세균 증식

문제 설명

어떤 세균은 1시간에 두배만큼 증식한다고 합니다. 처음 세균의 마리수 n과 경과한 시간 t가 매개변수로 주어질 때 t시간 후 세균의 수를 return하도록 solution 함수를 완성해주세요.

제한사항

문제풀이

1
2
3
function solution(n, t) {
  return n * Math.pow(2, t);
}

# Level 0. 한 번만 등장한 문자

한 번만 등장한 문자

문제 설명

문자열 s가 매개변수로 주어집니다. s에서 한 번만 등장하는 문자를 사전 순으로 정렬한 문자열을 return 하도록 solution 함수를 완성해보세요. 한 번만 등장하는 문자가 없을 경우 빈 문자열을 return 합니다.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
function solution(s) {
  let answer = "";
  const countArray = new Array(32).fill(0);
  for (let i = 0; i < s.length; i++) {
    countArray[s.charCodeAt(i) - 97]++;
  }
  countArray.forEach((count, index) => {
    if (count === 1) answer += String.fromCharCode(index + 97);
  });
  return answer;
}

# Level 0. 가장 큰 수 찾기

가장 큰 수 찾기

문제 설명

정수 배열 array가 매개변수로 주어질 때, 가장 큰 수와 그 수의 인덱스를 담은 배열을 return 하도록 solution 함수를 완성해보세요.

제한사항

문제 풀이

1
2
3
4
function solution(array) {
  const max = Math.max(...array);
  return [max, array.indexOf(max)];
}

# Level 0. 약수 구하기

약수 구하기

문제 설명

정수 n이 매개변수로 주어질 때, n의 약수를 오름차순으로 담은 배열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(n) {
  const answer = [];

  for (let i = 1; i <= Math.sqrt(n); i++) {
    if (n % i === 0) {
      answer.push(i);
      if (n / i === i) continue;
      answer.push(n / i);
    }
  }

  return answer.sort((a, b) => a - b);
}

# Level 0. 문자열 계산하기

문자열 계산하기

문제 설명

my_string은 “3 + 5”처럼 문자열로 된 수식입니다. 문자열 my_string이 매개변수로 주어질 때, 수식을 계산한 값을 return 하는 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
function solution(my_string) {
  const splitted = my_string.split(" ");
  let answer = parseInt(splitted[0]);
  for (let i = 1; i < splitted.length; i += 2) {
    if (splitted[i] === "+") {
      answer += parseInt(splitted[i + 1]);
    } else if (splitted[i] === "-") {
      answer -= parseInt(splitted[i + 1]);
    }
  }
  return answer;
}

# Level 0. 배열의 유사도

배열의 유사도

문제 설명

두 배열이 얼마나 유사한지 확인해보려고 합니다. 문자열 배열 s1과 s2가 주어질 때 같은 원소의 개수를 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
function solution(s1, s2) {
  const similarity = s1.filter((char) => s2.includes(char)).length;
  return similarity;
}

# Level 0. 영어가 싫어요

영어가 싫어요

문제 설명

영어가 싫은 머쓱이는 영어로 표기되어있는 숫자를 수로 바꾸려고 합니다. 문자열 numbers가 매개변수로 주어질 때, numbers를 정수로 바꿔 return 하도록 solution 함수를 완성해 주세요.

제한사항

문제 풀이

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
const numberMap = {
  zero: "0",
  one: "1",
  two: "2",
  three: "3",
  four: "4",
  five: "5",
  six: "6",
  seven: "7",
  eight: "8",
  nine: "9",
};

function solution(numbers) {
  let answer = "";
  let searchWord = "";

  for (let i = 0; i < numbers.length; i++) {
    searchWord += numbers[i];
    if (numberMap[searchWord]) {
      answer += numberMap[searchWord];
      searchWord = "";
    }
  }

  return answer * 1;
}

# Level 0. 인덱스 바꾸기

인덱스 바꾸기

문제 설명

문자열 my_string과 정수 num1, num2가 매개변수로 주어질 때, my_string에서 인덱스 num1과 인덱스 num2에 해당하는 문자를 바꾼 문자열을 return 하도록 solution 함수를 완성해보세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
function solution(my_string, num1, num2) {
  my_string = my_string.split("");
  let temp = "";
  temp = my_string[num1];
  my_string[num1] = my_string[num2];
  my_string[num2] = temp;
  return my_string.join("");
}

더 나은 풀이

1
2
3
4
5
function solution(my_string, num1, num2) {
  my_string = my_string.split("");
  [my_string[num1], my_string[num2]] = [my_string[num2], my_string[num1]];
  return my_string.join("");
}

# Level 0. 대문자와 소문자

대문자와 소문자

문제 설명

문자열 my_string이 매개변수로 주어질 때, 대문자는 소문자로 소문자는 대문자로 변환한 문자열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
function solution(my_string) {
  let answer = "";
  for (let i = 0; i < my_string.length; i++) {
    const asciiNum = my_string[i].charCodeAt();
    if (asciiNum <= 90) {
      answer += String.fromCharCode(asciiNum + 32);
    } else {
      answer += String.fromCharCode(asciiNum - 32);
    }
  }
  return answer;
}

# Level 0. 배열 원소의 길이

배열 원소의 길이

문제 설명

문자열 배열 strlist가 매개변수로 주어집니다. strlist 각 원소의 길이를 담은 배열을 retrun하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
function solution(strlist) {
  return strlist.map((str) => str.length);
}

# Level 0. 삼각형의 완성 조건 (1)

삼각형의 완성조건 (1)

문제 설명

선분 세 개로 삼각형을 만들기 위해서는 다음과 같은 조건을 만족해야 합니다.

가장 긴 변의 길이는 다른 두 변의 길이의 합보다 작아야 합니다. 삼각형의 세 변의 길이가 담긴 배열 sides이 매개변수로 주어집니다. 세 변으로 삼각형을 만들 수 있다면 1, 만들 수 없다면 2를 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
function solution(sides) {
  const max = Math.max(...sides);
  const maxIndex = sides.indexOf(max);
  const answer =
    sides.reduce((acc, curr, index) => {
      if (index === maxIndex) return acc;
      return acc + curr;
    }) > max
      ? 1
      : 2;
  return answer;
}

# Level 0. 중복된 문자 제거

중복된 문자 제거

문제 설명

문자열 my_string이 매개변수로 주어집니다. my_string에서 중복된 문자를 제거하고 하나의 문자만 남긴 문자열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
function solution(my_string) {
  return [...new Set(my_string.split(""))].join("");
}

# Level 0. 암호 해독

암호 해독

문제 설명

군 전략가 머쓱이는 전쟁 중 적군이 다음과 같은 암호 체계를 사용한다는 것을 알아냈습니다.

암호화된 문자열 cipher를 주고받습니다. 그 문자열에서 code의 배수 번째 글자만 진짜 암호입니다. 문자열 cipher와 정수 code가 매개변수로 주어질 때 해독된 암호 문자열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
function solution(cipher, code) {
  let answer = "";
  for (let i = 0; i < cipher.length; i++) {
    if ((i + 1) % code === 0) answer += cipher[i];
  }
  return answer;
}

# Level 0. 가까운 수

가까운 수

문제 설명

정수 배열 array와 정수 n이 매개변수로 주어질 때, array에 들어있는 정수 중 n과 가장 가까운 수를 return 하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function solution(array, n) {
  let closest;
  let gap = Infinity;
  array.forEach((num) => {
    const currGap = Math.abs(num - n);
    if (currGap < gap) {
      closest = num;
      gap = currGap;
    } else if (currGap === gap && num < closest) {
      closest = num;
    }
  });
  return closest;
}

# Level 0. 369게임

369게임

문제 설명

머쓱이는 친구들과 369게임을 하고 있습니다. 369게임은 1부터 숫자를 하나씩 대며 3, 6, 9가 들어가는 숫자는 숫자 대신 3, 6, 9의 개수만큼 박수를 치는 게임입니다. 머쓱이가 말해야하는 숫자 order가 매개변수로 주어질 때, 머쓱이가 쳐야할 박수 횟수를 return 하도록 solution 함수를 완성해보세요.

제한사항

문제 풀이

1
2
3
function solution(order) {
  return order.toString().match(/3|6|9/g)?.length || 0;
}

# Level 0. 문자열 정렬하기 (1)

문자열 정렬하기 (1)

문제 설명

문자열 my_string이 매개변수로 주어질 때, my_string 안에 있는 숫자만 골라 오름차순 정렬한 리스트를 return 하도록 solution 함수를 작성해보세요.

제한사항

문제 풀이

1
2
3
4
function solution(my_string) {
  const numbers = my_string.match(/[0-9]/g).map((num) => num * 1);
  return numbers.sort((a, b) => a - b);
}

# Level 0. 숨어있는 숫자의 덧셈 (1)

숨어있는 숫자의 덧셈 (1)

문제 설명

문자열 my_string이 매개변수로 주어집니다. my_string안의 모든 자연수들의 합을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
function solution(my_string) {
  const numbers = my_string.match(/[0-9]/g).map((num) => num * 1);
  const sum = numbers.reduce((acc, curr) => acc + curr);
  return sum;
}

# Level 0. 소인수분해

소인수분해

문제 설명

소인수분해란 어떤 수를 소수들의 곱으로 표현하는 것입니다. 예를 들어 12를 소인수 분해하면 2 _ 2 _ 3 으로 나타낼 수 있습니다. 따라서 12의 소인수는 2와 3입니다. 자연수 n이 매개변수로 주어질 때 n의 소인수를 오름차순으로 담은 배열을 return하도록 solution 함수를 완성해주세요.

제한사항

첫번째 풀이

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
const checkIsPrime = (number) => {
  for (let j = 2; j <= Math.floor(Math.sqrt(number)); j++) {
    if (number % j === 0 && number !== j) return false;
  }

  return true;
};

const getPrimesUnderNumber = (limitNumber) => {
  const primes = [2];

  for (let i = 3; i <= limitNumber; i++) {
    checkIsPrime(i) && primes.push(i);
  }

  return primes;
};

const solution = (n) => {
  const answer = [];
  const primes = getPrimesUnderNumber(n);

  let index = 0;
  while (primes[index] <= n) {
    if (n % primes[index] === 0) {
      answer.push(primes[index]);
    }
    index++;
  }

  return answer;
};

# Level 0. 모음 제거

모음 제거

문제 설명

영어에선 a, e, i, o, u 다섯 가지 알파벳을 모음으로 분류합니다. 문자열 my_string이 매개변수로 주어질 때 모음을 제거한 문자열을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
function solution(my_string) {
  const vowel = ["a", "e", "i", "o", "u"];
  return my_string
    .split("")
    .filter((char) => !vowel.includes(char))
    .join("");
}

# Level 0. 컨트롤 제트

컨트롤 제트

문제 설명

숫자와 “Z”가 공백으로 구분되어 담긴 문자열이 주어집니다. 문자열에 있는 숫자를 차례대로 더하려고 합니다. 이 때 “Z”가 나오면 바로 전에 더했던 숫자를 뺀다는 뜻입니다. 숫자와 “Z”로 이루어진 문자열 s가 주어질 때, 머쓱이가 구한 값을 return 하도록 solution 함수를 완성해보세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
function solution(s) {
  let answer = 0;
  s = s.split(" ");
  s.forEach((num, index) => {
    if (index === 0) return;
    if ((num === "Z") | (s[index - 1] === "Z")) return;
    answer += s[index - 1] * 1;
  });
  answer += s[s.length - 1] !== "Z" ? s[s.length - 1] * 1 : 0;
  return answer;
}

# Level 0. 공 던지기

공 던지기

문제 설명

머쓱이는 친구들과 동그랗게 서서 공 던지기 게임을 하고 있습니다. 공은 1번부터 던지며 오른쪽으로 한 명을 건너뛰고 그다음 사람에게만 던질 수 있습니다. 친구들의 번호가 들어있는 정수 배열 numbers와 정수 K가 주어질 때, k번째로 공을 던지는 사람의 번호는 무엇인지 return 하도록 solution 함수를 완성해보세요.

제한사항

풀이

1
2
3
4
5
const solution = (numbers, k) => {
  const passedLength = (k - 1) * 2;
  const index = passedLength % numbers.length;
  return numbers[index];
};

# Level 0. 합성수 찾기

합성수 찾기

문제 설명

약수의 개수가 세 개 이상인 수를 합성수라고 합니다. 자연수 n이 매개변수로 주어질 때 n이하의 합성수의 개수를 return하도록 solution 함수를 완성해주세요.

제한사항

첫번째 풀이

1
2
3
4
5
6
7
8
9
10
11
12
const solution = (n) => {
  let answer = 0;
  for (let i = 4; i <= n; i++) {
    for (let j = 2; j <= Math.floor(Math.sqrt(i)); j++) {
      if (i % j === 0) {
        answer++;
        break;
      }
    }
  }
  return answer;
};

두번째 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const solution = (n) => {
  const screener = new Array(n + 1).fill(false);
  for (let i = 2; i <= Math.floor(Math.sqrt(n)); i++) {
    if (screener[i]) continue;

    let multiplier = 2;
    while (i * multiplier <= n) {
      screener[i * multiplier] = true;
      multiplier++;
    }
  }

  return screener.filter((result) => result).length;
};

# Level 0. 문자열 나누기

문자열 나누기

문제 설명

문자열 s가 입력되었을 때 다음 규칙을 따라서 이 문자열을 여러 문자열로 분해하려고 합니다.

문자열 s가 매개변수로 주어질 때, 위 과정과 같이 문자열들로 분해하고, 분해한 문자열의 개수를 return 하는 함수 solution을 완성하세요.

첫번재 풀이

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
function solution(s) {
  let answer = 0;
  let currChar = "";
  let sameCount = 0;
  let diffCount = 0;
  let isSplitted = true;
  s.split("").forEach((char) => {
    if (isSplitted) {
      answer++;
      currChar = char;
      sameCount = 1;
      diffCount = 0;
      isSplitted = false;
      return;
    }

    if (currChar === char) {
      sameCount++;
    } else {
      diffCount++;
    }

    if (sameCount === diffCount) {
      isSplitted = true;
    }
  });
  return answer;
}

# Level 0. 주사위의 개수

주사위의 개수

문제 설명

머쓱이는 직육면체 모양의 상자를 하나 가지고 있는데 이 상자에 정육면체 모양의 주사위를 최대한 많이 채우고 싶습니다. 상자의 가로, 세로, 높이가 저장되어있는 배열 box와 주사위 모서리의 길이 정수 n이 매개변수로 주어졌을 때, 상자에 들어갈 수 있는 주사위의 최대 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항

풀이

1
2
3
4
5
const solution = (box, n) => {
  return (
    Math.floor(box[0] / n) * Math.floor(box[1] / n) * Math.floor(box[2] / n)
  );
};

# Level 0. 최댓값 만들기 (1)

최댓값 만들기 (1)

문제 설명

정수 배열 numbers가 매개변수로 주어집니다. numbers의 원소 중 두 개를 곱해 만들 수 있는 최댓값을 return하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
const solution = (numbers) => {
  let firstMax = 0;
  let secondMax = 0;
  numbers.forEach((number) => {
    if (numbers[i] > firstMax) {
      secondMax = firstMax;
      firstMax = numbers[i];
    } else if (numbers[i] > secondMax) {
      secondMax = numbers[i];
    }
  });
  return firstMax * secondMax;
};

# Level 0. 팩토리얼

팩토리얼

문제 설명

i팩토리얼 (i!)은 1부터 i까지 정수의 곱을 의미합니다. 예를들어 5! = 5 _ 4 _ 3 _ 2 _ 1 = 120 입니다. 정수 n이 주어질 때 다음 조건을 만족하는 가장 큰 정수 i를 return 하도록 solution 함수를 완성해주세요.

제한사항

문제 풀이

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
const solution = (n) => {
  const memo = new Array(10).fill(0);
  memo[0] = 1;
  memo[1] = 1;
  memo[2] = 2;

  const _memoFactorial = (number) => {
    if (memo[number]) return memo[number];

    memo[number] = number * _memoFactorial(number - 1);
    return memo[number];
  };
  _memoFactorial(10);

  let maxFactorial = 0;
  memo.some((num, index) => {
    if (num <= n) {
      maxFactorial = index;
      return false;
    }

    return true;
  });

  return maxFactorial;
};

# Level 0. 가장 가까운 같은 글자

가장 가까운 같은 글자

문제 설명

문자열 s가 주어졌을 때, s의 각 위치마다 자신보다 앞에 나왔으면서, 자신과 가장 가까운 곳에 있는 같은 글자가 어디 있는지 알고 싶습니다. 예를 들어, s=”banana”라고 할 때, 각 글자들을 왼쪽부터 오른쪽으로 읽어 나가면서 다음과 같이 진행할 수 있습니다.

문자열 s이 주어질 때, 위와 같이 정의된 연산을 수행하는 함수 solution을 완성해주세요.

제한사항

문제풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function solution(s) {
  const answer = [];
  const charMap = {};
  s.split("").forEach((char, index) => {
    if (charMap[char] === undefined) {
      answer[index] = -1;
      charMap[char] = index;
      return;
    }

    answer[index] = index - charMap[char];
    charMap[char] = index;
  });
  return answer;
}

[백준 문제풀이] 14890번 경사로


14890번 경사로 (Gold 3)

문제

https://www.acmicpc.net/problem/14890

크기가 N×N인 지도가 있다. 지도의 각 칸에는 그 곳의 높이가 적혀져 있다.

오늘은 이 지도에서 지나갈 수 있는 길이 몇 개 있는지 알아보려고 한다. 길이란 한 행 또는 한 열 전부를 나타내며, 한쪽 끝에서 다른쪽 끝까지 지나가는 것이다.

다음과 같은 N=6인 경우 지도를 살펴보자.

이때, 길은 총 2N개가 있으며, 아래와 같다.

길을 지나갈 수 있으려면 길에 속한 모든 칸의 높이가 모두 같아야 한다. 또는, 경사로를 놓아서 지나갈 수 있는 길을 만들 수 있다. 경사로는 높이가 항상 1이며, 길이는 L이다. 또, 개수는 매우 많아 부족할 일이 없다. 경사로는 낮은 칸과 높은 칸을 연결하며, 아래와 같은 조건을 만족해야한다.

경사로는 낮은 칸에 놓으며, L개의 연속된 칸에 경사로의 바닥이 모두 접해야 한다. 낮은 칸과 높은 칸의 높이 차이는 1이어야 한다. 경사로를 놓을 낮은 칸의 높이는 모두 같아야 하고, L개의 칸이 연속되어 있어야 한다. 아래와 같은 경우에는 경사로를 놓을 수 없다.

경사로를 놓은 곳에 또 경사로를 놓는 경우 낮은 칸과 높은 칸의 높이 차이가 1이 아닌 경우 낮은 지점의 칸의 높이가 모두 같지 않거나, L개가 연속되지 않은 경우 경사로를 놓다가 범위를 벗어나는 경우 L = 2인 경우에 경사로를 놓을 수 있는 경우를 그림으로 나타내면 아래와 같다.

경사로를 놓을 수 없는 경우는 아래와 같다.

위의 그림의 가장 왼쪽부터 1번, 2번, 3번, 4번 예제라고 했을 때, 1번은 높이 차이가 1이 아니라서, 2번은 경사로를 바닥과 접하게 놓지 않아서, 3번은 겹쳐서 놓아서, 4번은 기울이게 놓아서 불가능한 경우이다.

가장 위에 주어진 그림 예의 경우에 지나갈 수 있는 길은 파란색으로, 지나갈 수 없는 길은 빨간색으로 표시되어 있으며, 아래와 같다. 경사로의 길이 L = 2이다.

지도가 주어졌을 때, 지나갈 수 있는 길의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N (2 ≤ N ≤ 100)과 L (1 ≤ L ≤ N)이 주어진다. 둘째 줄부터 N개의 줄에 지도가 주어진다. 각 칸의 높이는 10보다 작거나 같은 자연수이다.

출력

첫째 줄에 지나갈 수 있는 길의 개수를 출력한다.

소스코드

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const fs = require("fs");
const [[N, L], ...board] = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split("\n")
  .map((line) => line.split(" ").map((el) => el * 1));

let answer = 0;

for (let i = 0; i < N; i++) {
  runRoad(i, "column");
  runRoad(i, "row");
}

console.log(answer);

function runRoad(index, direction) {
  let gapFlag = false;
  let connectCount = 1;

  // 길 한 칸씩 진행
  for (let j = 1; j < N; j++) {
    const gap = getGap(index, j, direction);

    // 단차에 따라 다르게 진행
    if (gap === 0) {
      // 단차가 없는 경우
      // 단차가 없는 평지라면, 계수한다.
      connectCount++;
    } else if (Math.abs(gap) === 1) {
      // 단차가 있는 경우
      // 해결되지 못한 단차가 있었다면 탐색 종료
      if (gapFlag) return;

      if (gap === 1) {
        // 높이가 1 증가한 경우

        // 현재까지 connect 길이가
        // 경사로를 놓을 수 없다면, 탐색을 종료한다.
        if (connectCount < L) return;
      } else {
        // 높이가 1 감소한 경우
        // 단차를 true로 바꿔주고
        // 가능성을 보아 기회를 한 번 더 준다.
        gapFlag = true;
      }

      connectCount = 1;
    } else {
      // 단차가 2 이상이면, 탐색 종료
      return;
    }

    if (gapFlag && connectCount >= L) {
      // 단차가 있었는데 경사로를 둘 길이가 된다면,
      // gapFlag를 false로 바꾸고
      // 연속된 평지 길이를 0으로 초기화한다.
      gapFlag = false;
      connectCount = 0;
    }
  }

  // 마지막으로 단차가 남았는지 확인 후,
  // 모든 역경과 시련을 이겨냈다면
  // 통과시켜준다.
  if (!gapFlag) {
    answer++;
  }
}

function getGap(fixed, variable, direction) {
  if (direction === "column") {
    const gap = board[fixed][variable] - board[fixed][variable - 1];
    if (Math.abs(gap) > 1) {
      return 2;
    } else if (gap === 1) {
      return 1;
    } else if (gap === -1) {
      return -1;
    } else {
      return 0;
    }
  }

  if (direction === "row") {
    const gap = board[variable][fixed] - board[variable - 1][fixed];
    if (Math.abs(gap) > 1) {
      return 2;
    } else if (gap === 1) {
      return 1;
    } else if (gap === -1) {
      return -1;
    } else {
      return 0;
    }
  }
}

[백준 문제풀이] 8979번 올림픽


8979번 올림픽 (Silver 3)

문제

https://www.acmicpc.net/problem/8979

올림픽은 참가에 의의가 있기에 공식적으로는 국가간 순위를 정하지 않는다. 그러나, 많은 사람들이 자신의 국가가 얼마나 잘 하는지에 관심이 많기 때문에 비공식적으로는 국가간 순위를 정하고 있다. 두 나라가 각각 얻은 금, 은, 동메달 수가 주어지면, 보통 다음 규칙을 따라 어느 나라가 더 잘했는지 결정한다.

금메달 수가 더 많은 나라 금메달 수가 같으면, 은메달 수가 더 많은 나라 금, 은메달 수가 모두 같으면, 동메달 수가 더 많은 나라 각 국가는 1부터 N 사이의 정수로 표현된다. 한 국가의 등수는 (자신보다 더 잘한 나라 수) + 1로 정의된다. 만약 두 나라가 금, 은, 동메달 수가 모두 같다면 두 나라의 등수는 같다. 예를 들어, 1번 국가가 금메달 1개, 은메달 1개를 얻었고, 2번 국가와 3번 국가가 모두 은메달 1개를 얻었으며, 4번 국가는 메달을 얻지 못하였다면, 1번 국가가 1등, 2번 국가와 3번 국가가 공동 2등, 4번 국가가 4등이 된다. 이 경우 3등은 없다.

각 국가의 금, 은, 동메달 정보를 입력받아서, 어느 국가가 몇 등을 했는지 알려주는 프로그램을 작성하시오.

입력

입력의 첫 줄은 국가의 수 N(1 ≤ N ≤ 1,000)과 등수를 알고 싶은 국가 K(1 ≤ K ≤ N)가 빈칸을 사이에 두고 주어진다. 각 국가는 1부터 N 사이의 정수로 표현된다. 이후 N개의 각 줄에는 차례대로 각 국가를 나타내는 정수와 이 국가가 얻은 금, 은, 동메달의 수가 빈칸을 사이에 두고 주어진다. 전체 메달 수의 총합은 1,000,000 이하이다.

출력

출력은 단 한 줄이며, 입력받은 국가 K의 등수를 하나의 정수로 출력한다. 등수는 반드시 문제에서 정의된 방식을 따라야 한다.

소스코드

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 입력 값 처리 부분
const fs = require("fs");
const [numbers, ...medals] = fs
  .readFileSync("/dev/stdin")
  .toString()
  .split("\n")
  .map((line) => line.split(" ").map((el) => el * 1));
const [N, K] = numbers;

solveProblem();

// 문제 해결 방식
// 1. 주어진 K 값으로 탐색하여 target을 구한다.
// 2. target과 타국들과의 메달 비교를 통해 rank를 도출.
function solveProblem() {
  const target = getTarget(K, medals);
  const rank = getRank(target, medals);

  console.log(rank);
}

// target 국가를 구하는 함수
// 주어진 K를 전체 국가의 ID과 비교하여 찾는다.
// @return {Array}: target 국가 정보
function getTarget(K, medals) {
  let result;
  for (let i = 0; i < N; i++) {
    if (medals[i][0] === K) result = medals[i];
  }

  return result;
}

// rank를 구하는 함수
// 금메달 비교 후 target 국가보다 많으면 rank + 1
// 금메달이 같으면, 은메달 비교 후 target 국가보다 많으면 rank + 1
// 은메달이 같으면, 동메달 비교 후 target 국가보다 많으면 rank + 1
// @return {number}: 최종 rank를 반환
function getRank(target, medals) {
  let result = 1;

  // 전체 국가와 비교
  for (let i = 0; i < N; i++) {
    // 본인 국가는 지나가기
    if (medals[i][0] === target[0]) continue;

    if (medals[i][1] >= target[1]) {
      // 금메달 비교
      if (medals[i][1] > target[1]) {
        result++;
      } else if (medals[i][2] >= target[2]) {
        // 은메달 비교
        if (medals[i][2] > target[2]) {
          result++;
        } else if (medals[i][3] >= target[3]) {
          // 동메달 비교
          if (medals[i][3] > target[3]) {
            result++;
          }
        }
      }
    }
  }

  return result;
}

[백준 문제풀이] 1063번 킹


1063번 킹 (Silver 3)

문제

https://www.acmicpc.net/problem/1063

8*8크기의 체스판에 왕이 하나 있다. 킹의 현재 위치가 주어진다. 체스판에서 말의 위치는 다음과 같이 주어진다. 알파벳 하나와 숫자 하나로 이루어져 있는데, 알파벳은 열을 상징하고, 숫자는 행을 상징한다. 열은 가장 왼쪽 열이 A이고, 가장 오른쪽 열이 H까지 이고, 행은 가장 아래가 1이고 가장 위가 8이다. 예를 들어, 왼쪽 아래 코너는 A1이고, 그 오른쪽 칸은 B1이다.

킹은 다음과 같이 움직일 수 있다.

R : 한 칸 오른쪽으로 L : 한 칸 왼쪽으로 B : 한 칸 아래로 T : 한 칸 위로 RT : 오른쪽 위 대각선으로 LT : 왼쪽 위 대각선으로 RB : 오른쪽 아래 대각선으로 LB : 왼쪽 아래 대각선으로 체스판에는 돌이 하나 있는데, 돌과 같은 곳으로 이동할 때는, 돌을 킹이 움직인 방향과 같은 방향으로 한 칸 이동시킨다. 아래 그림을 참고하자.

입력으로 킹이 어떻게 움직여야 하는지 주어진다. 입력으로 주어진 대로 움직여서 킹이나 돌이 체스판 밖으로 나갈 경우에는 그 이동은 건너 뛰고 다음 이동을 한다.

킹과 돌의 마지막 위치를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 킹의 위치, 돌의 위치, 움직이는 횟수 N이 주어진다. 둘째 줄부터 N개의 줄에는 킹이 어떻게 움직여야 하는지 주어진다. N은 50보다 작거나 같은 자연수이고, 움직이는 정보는 위에 쓰여 있는 8가지 중 하나이다.

출력

첫째 줄에 킹의 마지막 위치, 둘째 줄에 돌의 마지막 위치를 출력한다.

소스코드

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const fs = require("fs");
let [chessPieces, ...commands] = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split("\n");

chessPieces = chessPieces.split(" ");
const N = chessPieces.pop();

solveProblem();

function solveProblem() {
  // 이동을 숫자의 증감으로 표현할 수 있도록
  // 열을 숫자로 변환함.
  chessPieces = positionToAscii(chessPieces);

  // 명령 배열을 돌며 명령 수행
  commands.forEach((command) => {
    chessPieces = handleCommand(chessPieces, command);
  });

  // 출력을 위해 다시 체스 포지션 문자열로 변경
  chessPieces = asciiToPosition(chessPieces);

  // 결과 출력
  console.log(chessPieces.join("\n"));
}

// 명령 수행
// @chessPieces {Array}: king, stone의 위치 정보를 담은 이차원 배열
// @command {String}: 명령 문자열
// @return {Array}: king, stone의 위치 정보를 담고 있는 이차원 배열
function handleCommand(chessPieces, command) {
  let [king, stone] = chessPieces;

  // king 이동.
  king = moveChessPiece(king, command);
  if (king[0] === stone[0] && king[1] === stone[1]) {
    // king과 store의 위치가 같으면,
    // stone도 이동시킴.
    stone = moveChessPiece(stone, command);
  }

  // 두 말의 위치가 보드 내에 있다면,
  // 변경된 위치 정보 반환
  if (checkPosition(...king) && checkPosition(...stone)) return [king, stone];

  // 두 말의 위치가 보드 밖에 있다면,
  // 변경전 위치 정보 반환
  return chessPieces;
}

// 체스말 이동 함수
// @chessPiece {Array} : 이동할 체스말 위치 정보
// @command {String}: 명령 문자열
function moveChessPiece(chessPiece, command) {
  let [movedColumn, movedRow] = chessPiece;

  switch (command[0]) {
    case "R":
      movedColumn++;
      break;
    case "L":
      movedColumn--;
      break;
    case "T":
      movedRow++;
      break;
    case "B":
      movedRow--;
      break;
  }

  if (command[1]) {
    switch (command[1]) {
      case "T":
        movedRow++;
        break;
      case "B":
        movedRow--;
        break;
    }
  }

  return [movedColumn, movedRow];
}

// 위치 확인 함수
// 체스말의 위치가 보드 내에 있는지 확인
// @column, row {String}: 체스말 열, 행 정보
// @return {Boolean}
function checkPosition(column, row) {
  if (column >= 65 && column <= 72 && row >= 1 && row <= 8) return true;

  return false;
}

// 체스판 위치 => 아스키코드 변환 함수
// @return {Array} : 아스키코드로 변환된 king, stone의 위치 정보 이차원 배열
function positionToAscii(chessPieces) {
  chessPieces = chessPieces.map((chessPiece) => {
    chessPiece = chessPiece.split("");
    chessPiece[0] = chessPiece[0].charCodeAt();
    chessPiece[1] = chessPiece[1] * 1;

    return chessPiece;
  });

  return chessPieces;
}

// 아스키코드 => 체스판 위치 변환 함수
// @return {Array} : 문자열로 변환된 king, stone의 위치 정보 배열
function asciiToPosition(chessPieces) {
  chessPieces = chessPieces.map((chessPiece) => {
    chessPiece[0] = String.fromCharCode(chessPiece[0]);
    chessPiece[1] = chessPiece[1] * 1;

    return chessPiece.join("");
  });

  return chessPieces;
}

[백준 문제풀이] 10798번 세로읽기


10798번 세로읽기 (Bronze 1)

문제

https://www.acmicpc.net/problem/10798

아직 글을 모르는 영석이가 벽에 걸린 칠판에 자석이 붙어있는 글자들을 붙이는 장난감을 가지고 놀고 있다.

이 장난감에 있는 글자들은 영어 대문자 ‘A’부터 ‘Z’, 영어 소문자 ‘a’부터 ‘z’, 숫자 ‘0’부터 ‘9’이다. 영석이는 칠판에 글자들을 수평으로 일렬로 붙여서 단어를 만든다. 다시 그 아래쪽에 글자들을 붙여서 또 다른 단어를 만든다. 이런 식으로 다섯 개의 단어를 만든다. 아래 그림 1은 영석이가 칠판에 붙여 만든 단어들의 예이다.

A A B C D D a f z z 0 9 1 2 1 a 8 E W g 6 P 5 h 3 k x

한 줄의 단어는 글자들을 빈칸 없이 연속으로 나열해서 최대 15개의 글자들로 이루어진다. 또한 만들어진 다섯 개의 단어들의 글자 개수는 서로 다를 수 있다.

심심해진 영석이는 칠판에 만들어진 다섯 개의 단어를 세로로 읽으려 한다. 세로로 읽을 때, 각 단어의 첫 번째 글자들을 위에서 아래로 세로로 읽는다. 다음에 두 번째 글자들을 세로로 읽는다. 이런 식으로 왼쪽에서 오른쪽으로 한 자리씩 이동 하면서 동일한 자리의 글자들을 세로로 읽어 나간다. 위의 그림 1의 다섯 번째 자리를 보면 두 번째 줄의 다섯 번째 자리의 글자는 없다. 이런 경우처럼 세로로 읽을 때 해당 자리의 글자가 없으면, 읽지 않고 그 다음 글자를 계속 읽는다. 그림 1의 다섯 번째 자리를 세로로 읽으면 D1gk로 읽는다.

그림 1에서 영석이가 세로로 읽은 순서대로 글자들을 공백 없이 출력하면 다음과 같다:

Aa0aPAf985Bz1EhCz2W3D1gkD6x

칠판에 붙여진 단어들이 주어질 때, 영석이가 세로로 읽은 순서대로 글자들을 출력하는 프로그램을 작성하시오.

입력

총 다섯줄의 입력이 주어진다. 각 줄에는 최소 1개, 최대 15개의 글자들이 빈칸 없이 연속으로 주어진다. 주어지는 글자는 영어 대문자 ‘A’부터 ‘Z’, 영어 소문자 ‘a’부터 ‘z’, 숫자 ‘0’부터 ‘9’ 중 하나이다. 각 줄의 시작과 마지막에 빈칸은 없다.

출력

영석이가 세로로 읽은 순서대로 글자들을 출력한다. 이때, 글자들을 공백 없이 연속해서 출력한다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const fs = require("fs");
const words = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split("\n")
  .map((el) => el.split(""));

let answer = "";

for (let j = 0; j < 15; j++) {
  for (let i = 0; i < words.length; i++) {
    if (words[i][j]) answer += words[i][j];
  }
}

console.log(answer);

boj_14500 테트로미노


문제

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.

정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다. 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.

tetromino

아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.

테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.

테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.

입력

첫째 줄에 종이의 세로 크기 N과 가로 크기 M이 주어진다. (4 ≤ N, M ≤ 500)

둘째 줄부터 N개의 줄에 종이에 쓰여 있는 수가 주어진다. i번째 줄의 j번째 수는 위에서부터 i번째 칸, 왼쪽에서부터 j번째 칸에 쓰여 있는 수이다. 입력으로 주어지는 수는 1,000을 넘지 않는 자연수이다.

출력

첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다.

느낀 점

참 쉽지 않았다.

내 코드에 대한 자신감이 많이 떨어져 있던 것 같다. 전체적으로 우울감이 동반되는 바람에 논리성이 부족해졌다고 생각할 수 있었다.

처음부터 끝까지 머리써서 다시 푸니 괜찮았다.

풀이 코드

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const [N, M] = input
  .shift()
  .split(" ")
  .map((el) => parseInt(el));
const board = input.map((line) => line.split(" ").map((el) => parseInt(el)));

let max = 0;

let visited = new Array(N);
for (let i = 0; i < N; i++) {
  visited[i] = new Array(M);
}

let awesomeVisited = new Array(N);
for (let i = 0; i < N; i++) {
  awesomeVisited[i] = new Array(M);
}

for (let i = 0; i < N; i++) {
  for (let j = 0; j < M; j++) {
    dfs(0, 0, i, j);
  }
}

console.log(max);

function dfs(depth, answer, x, y) {
  if (depth === 4) {
    if (max < answer) {
      max = answer;
    }
    return;
  }

  let dx = [1, -1, 0, 0];
  let dy = [0, 0, 1, -1];

  answer += board[x][y];
  visited[x][y] = true;

  checkAwesomeShape(x, y);

  for (let i = 0; i < 4; i++) {
    const newX = x + dx[i];
    const newY = y + dy[i];
    if (isRange(newX, newY)) {
      if (visited[newX][newY]) continue;
      checkAwesomeShape(x, y);
      dfs(depth + 1, answer, newX, newY);
    }
  }
  answer -= board[x][y];
  visited[x][y] = false;
}

function isRange(x, y) {
  if (x >= 0 && y >= 0 && x < N && y < M) {
    return true;
  } else {
    return false;
  }
}

function checkAwesomeShape(x, y) {
  let upward;
  let downward;
  let right;
  let left;

  // 윗방향
  if (isRange(x, y + 1)) {
    upward = board[x][y + 1];
  }
  // 아랫방향
  if (isRange(x, y - 1)) {
    downward = board[x][y - 1];
  }
  // 오른쪽방향
  if (isRange(x + 1, y)) {
    right = board[x + 1][y];
  }
  // 왼쪽방향
  if (isRange(x - 1, y)) {
    left = board[x - 1][y];
  }

  if (upward && right && left) {
    const upwardShape = board[x][y] + upward + right + left;
    if (max < upwardShape) max = upwardShape;
  }

  if (upward && right && downward) {
    const rightShape = board[x][y] + upward + right + downward;
    if (max < rightShape) max = rightShape;
  }

  if (left && right && downward) {
    const downShape = board[x][y] + left + right + downward;
    if (max < downShape) max = downShape;
  }

  if (upward && left && downward) {
    const leftShape = board[x][y] + left + upward + downward;
    if (max < leftShape) max = leftShape;
  }
}

[백준 문제풀이] 17298번 오큰수


17298번 오큰수 (Gold 4)

문제

https://www.acmicpc.net/problem/17298

크기가 N인 수열 A = A1, A2, …, AN이 있다. 수열의 각 원소 Ai에 대해서 오큰수 NGE(i)를 구하려고 한다. Ai의 오큰수는 오른쪽에 있으면서 Ai보다 큰 수 중에서 가장 왼쪽에 있는 수를 의미한다. 그러한 수가 없는 경우에 오큰수는 -1이다.

예를 들어, A = [3, 5, 2, 7]인 경우 NGE(1) = 5, NGE(2) = 7, NGE(3) = 7, NGE(4) = -1이다. A = [9, 5, 4, 8]인 경우에는 NGE(1) = -1, NGE(2) = 8, NGE(3) = 8, NGE(4) = -1이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, …, AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

출력

총 N개의 수 NGE(1), NGE(2), …, NGE(N)을 공백으로 구분해 출력한다.

소스코드

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
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = parseInt(input[0]);
const numbers = input[1].split(" ").map((el) => +el);

const getNGE = (n, numArr) => {
  const stack = [];
  for (let i = 0; i < n; i++) {
    while (stack.length && numArr[stack[stack.length - 1]] < numArr[i]) {
      numArr[stack.pop()] = numArr[i];
    }
    stack.push(i);
  }

  while (stack.length) {
    numArr[stack.pop()] = -1;
  }

  return numArr;
};

const answer = getNGE(N, numbers);

console.log(answer.join(" "));

[백준 문제풀이] 10799번 쇠막대기


10799번 쇠막대기 (Bronze 1)

문제

https://www.acmicpc.net/problem/10799

여러 개의 쇠막대기를 레이저로 절단하려고 한다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자른다. 쇠막대기와 레이저의 배치는 다음 조건을 만족한다.

쇠막대기는 자신보다 긴 쇠막대기 위에만 놓일 수 있다. - 쇠막대기를 다른 쇠막대기 위에 놓는 경우 완전히 포함되도록 놓되, 끝점은 겹치지 않도록 놓는다. 각 쇠막대기를 자르는 레이저는 적어도 하나 존재한다. 레이저는 어떤 쇠막대기의 양 끝점과도 겹치지 않는다. 아래 그림은 위 조건을 만족하는 예를 보여준다. 수평으로 그려진 굵은 실선은 쇠막대기이고, 점은 레이저의 위치, 수직으로 그려진 점선 화살표는 레이저의 발사 방향이다.

이러한 레이저와 쇠막대기의 배치는 다음과 같이 괄호를 이용하여 왼쪽부터 순서대로 표현할 수 있다.

레이저는 여는 괄호와 닫는 괄호의 인접한 쌍 ‘( ) ’ 으로 표현된다. 또한, 모든 ‘( ) ’는 반드시 레이저를 표현한다. 쇠막대기의 왼쪽 끝은 여는 괄호 ‘ ( ’ 로, 오른쪽 끝은 닫힌 괄호 ‘) ’ 로 표현된다. 위 예의 괄호 표현은 그림 위에 주어져 있다.

쇠막대기는 레이저에 의해 몇 개의 조각으로 잘려지는데, 위 예에서 가장 위에 있는 두 개의 쇠막대기는 각각 3개와 2개의 조각으로 잘려지고, 이와 같은 방식으로 주어진 쇠막대기들은 총 17개의 조각으로 잘려진다.

쇠막대기와 레이저의 배치를 나타내는 괄호 표현이 주어졌을 때, 잘려진 쇠막대기 조각의 총 개수를 구하는 프로그램을 작성하시오.

입력

한 줄에 쇠막대기와 레이저의 배치를 나타내는 괄호 표현이 공백없이 주어진다. 괄호 문자의 개수는 최대 100,000이다.

출력

잘려진 조각의 총 개수를 나타내는 정수를 한 줄에 출력한다.

풀이

=> )를 pop의 기준으로 사용하는 스택을 작성해서 풀이했다.

소스코드

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

const stack = [];
let cutNum = 1;
let answer = 0;

for (let i = 0; i < input.length; i++) {
  switch (input[i]) {
    case ")":
      if (input[i - 1] === "(") {
        stack.pop();
        answer += stack.length;
      } else if (stack.length) {
        stack.pop();
        answer++;
      }
      break;
    default:
      stack.push(input[i]);
  }
}

console.log(answer);

[백준 문제풀이] 2609번 최대공약수와 최소공배수


2609번 최대공약수와 최소공배수 (Bronze 1)

문제

https://www.acmicpc.net/problem/2609

두 개의 자연수를 입력받아 최대 공약수와 최소 공배수를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에는 두 개의 자연수가 주어진다. 이 둘은 10,000이하의 자연수이며 사이에 한 칸의 공백이 주어진다.

출력

첫째 줄에는 입력으로 주어진 두 수의 최대공약수를, 둘째 줄에는 입력으로 주어진 두 수의 최소 공배수를 출력한다.

풀이

오늘은 알고리즘 문제에 집중이 되지 않아서, 자꾸 에러가 나고 코드가 예쁘지가 않아 최소공배수와 최대공약수의 개념을 찾아보았다.

최소공배수를 구하는 방법으로 신기한 것을 알 수 있었는데 gcd : 최대공약수, lcm : 최소공배수 a _ b = gcd _ lcm이라는 식을 통해서 lcm = (a*b) / gcd를 통해 구할 수 있다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");
input = input.map((el) => parseInt(el));

let a = input[0];
let b = input[1];

if (a < b) {
  let a = input[1];
  let b = input[0];
}

let gcd = 1;

for (let i = 2; i <= b; i++) {
  if (a % i === 0 && b % i === 0) {
    gcd = i;
  }
}

const lcm = (a * b) / gcd;

console.log(gcd);
console.log(lcm);

[백준 문제풀이] 17413번 단어 뒤집기 2


17413번 단어 뒤집기 2 (Silver 3)

문제

https://www.acmicpc.net/problem/17413

문자열 S가 주어졌을 때, 이 문자열에서 단어만 뒤집으려고 한다.

먼저, 문자열 S는 아래와과 같은 규칙을 지킨다.

알파벳 소문자(‘a’-‘z’), 숫자(‘0’-‘9’), 공백(‘ ‘), 특수 문자(‘<’, ‘>’)로만 이루어져 있다. 문자열의 시작과 끝은 공백이 아니다. ‘<’와 ‘>’가 문자열에 있는 경우 번갈아가면서 등장하며, ‘<’이 먼저 등장한다. 또, 두 문자의 개수는 같다. 태그는 ‘<’로 시작해서 ‘>’로 끝나는 길이가 3 이상인 부분 문자열이고, ‘<’와 ‘>’ 사이에는 알파벳 소문자와 공백만 있다. 단어는 알파벳 소문자와 숫자로 이루어진 부분 문자열이고, 연속하는 두 단어는 공백 하나로 구분한다. 태그는 단어가 아니며, 태그와 단어 사이에는 공백이 없다.

입력

첫째 줄에 문자열 S가 주어진다. S의 길이는 100,000 이하이다.

출력

첫째 줄에 문자열 S의 단어를 뒤집어서 출력한다.

풀이

switch case로 stack을 만들어서 풀었다.

소스코드

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
40
41
42
43
44
45
46
47
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim();

const stack = [];
let isTag = false;
let answerStr = "";

function reverseStr(str) {
  switch (str) {
    case "<":
      while (stack.length) {
        answerStr += stack.pop();
      }
      answerStr += str;
      isTag = true;
      break;
    case ">":
      answerStr += str;
      isTag = false;
      break;
    case " ":
      if (!isTag) {
        while (stack.length) {
          answerStr += stack.pop();
        }
      }
      answerStr += str;
      break;
    case undefined:
      while (stack.length) {
        answerStr += stack.pop();
      }
      break;
    default:
      if (isTag) {
        answerStr += str;
      } else {
        stack.push(str);
      }
  }
}

for (let i = 0; i <= input.length; i++) {
  reverseStr(input[i]);
}

console.log(answerStr);

[백준 문제풀이] 10866번 덱


10866번 덱 (Silver 4)

문제

https://www.acmicpc.net/problem/10866

정수를 저장하는 덱(Deque)를 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.

명령은 총 여덟 가지이다.

push_front X: 정수 X를 덱의 앞에 넣는다. push_back X: 정수 X를 덱의 뒤에 넣는다. pop_front: 덱의 가장 앞에 있는 수를 빼고, 그 수를 출력한다. 만약, 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다. pop_back: 덱의 가장 뒤에 있는 수를 빼고, 그 수를 출력한다. 만약, 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다. size: 덱에 들어있는 정수의 개수를 출력한다. empty: 덱이 비어있으면 1을, 아니면 0을 출력한다. front: 덱의 가장 앞에 있는 정수를 출력한다. 만약 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다. back: 덱의 가장 뒤에 있는 정수를 출력한다. 만약 덱에 들어있는 정수가 없는 경우에는 –1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

풀이

N이 10,000 이하라서 배열을 만들고 shift, pop을 사용해서 풀이했다.

소스코드

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
40
41
42
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = parseInt(input[0]);
const answerArr = [];
const deque = [];

const workDeque = (lineData) => {
  switch (lineData[0]) {
    case "pop_front":
      answerArr.push(deque.length ? deque.shift() : -1);
      break;
    case "pop_back":
      answerArr.push(deque.length ? deque.pop() : -1);
      break;
    case "size":
      answerArr.push(deque.length);
      break;
    case "empty":
      answerArr.push(deque.length ? 0 : 1);
      break;
    case "front":
      answerArr.push(deque.length ? deque[0] : -1);
      break;
    case "back":
      answerArr.push(deque.length ? deque[deque.length - 1] : -1);
      break;
    case "push_back":
      deque.push(lineData[1]);
      break;
    default:
      deque.unshift(lineData[1]);
      break;
  }
};

for (let i = 1; i <= N; i++) {
  const line = input[i].split(" ");
  workDeque(line);
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 1874번 스택수열


1874번 스택수열 (Silver 3)

문제

https://www.acmicpc.net/problem/1874

스택 (stack)은 기본적인 자료구조 중 하나로, 컴퓨터 프로그램을 작성할 때 자주 이용되는 개념이다. 스택은 자료를 넣는 (push) 입구와 자료를 뽑는 (pop) 입구가 같아 제일 나중에 들어간 자료가 제일 먼저 나오는 (LIFO, Last in First out) 특성을 가지고 있다.

1부터 n까지의 수를 스택에 넣었다가 뽑아 늘어놓음으로써, 하나의 수열을 만들 수 있다. 이때, 스택에 push하는 순서는 반드시 오름차순을 지키도록 한다고 하자. 임의의 수열이 주어졌을 때 스택을 이용해 그 수열을 만들 수 있는지 없는지, 있다면 어떤 순서로 push와 pop 연산을 수행해야 하는지를 알아낼 수 있다. 이를 계산하는 프로그램을 작성하라.

입력

첫 줄에 n (1 ≤ n ≤ 100,000)이 주어진다. 둘째 줄부터 n개의 줄에는 수열을 이루는 1이상 n이하의 정수가 하나씩 순서대로 주어진다. 물론 같은 정수가 두 번 나오는 일은 없다.

출력

입력된 수열을 만들기 위해 필요한 연산을 한 줄에 한 개씩 출력한다. push연산은 +로, pop 연산은 -로 표현하도록 한다. 불가능한 경우 NO를 출력한다.

첫번째 풀이

입력 숫자가 n번째 수보다 크면 같아질 때까지 push하고, 작으면 pop을 하는 방법으로 풀이했다. 방법이 없을때를 고려해서 try, catch를 사용했다.

첫번째 풀이 소스코드 (메모리 초과)

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 fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const n = parseInt(input.shift());
const stack = [];
const answerArr = [];
let isExist = true;

for (let j = 0; j < n; j++) {
  let num = 1;
  while (num <= n) {
    if (num <= parseInt(input[j])) {
      while (num <= parseInt(input[j])) {
        stack.push(num);
        answerArr.push("+");
        num++;
      }
    } else {
      try {
        while (stack[stack.length - 1] !== parseInt(input[j])) {
          stack.pop();
          answerArr.push("-");
        }
        stack.pop();
        answerArr.push("-");
      } catch {
        isExist = false;
      }
    }
  }
}

if (!isExist) {
  console.log("NO");
} else {
  console.log(answerArr.join("\n"));
}

최종 풀이

문제에서 요구하는 로직을 다시 정리해보자면,

  1. 1부터 n까지의 수를 오름차순으로 스택에 넣어야한다.
  2. 그 과정에서 제시되는 숫자 순서대로 스택에서 숫자를 pop 한다.
  3. 이것이 가능하다면 push와 pop의 로그를 출력하고,
  4. 불가능하다면 ‘NO’를 출력해라.

라고 정리할 수 있겠다.

이 알고리즘은

  1. stack에 순서대로 push 하고 pop하는 알고리즘
  2. 해당 숫자열이 스택 수열로 가능한지 확인하는 조건문

두 가지로 나눠볼 수 있겠다.

1. stack에 순서대로 push 하고 pop하는 알고리즘

먼저 오름차순대로 스택에 입력하고, 제시되는 순서대로 pop 하는 알고리즘을 생각해본다면.

  1. 1부터 첫번째 제시된 숫자까지 stack에 push 한다.
  2. 두번째로 제시된 숫자가 첫번째로 제시된 숫자보다 작으면, 두번째로 제시된 숫자까지 pop한다.
  3. 두번째로 제시된 숫자가 첫번째로 제시된 숫자보다 크면, 두번째로 제시된 숫자까지 stack에 push 한다.

위의 과정을 반복하면 된다.

2. 해당 숫자열이 스택 수열로 가능한지 확인하는 조건문

stack에 순서대로 push 하고 pop하는 알고리즘이 진행될 때, 스택 수열로 가능한지에 대한 조건문이 사용되어야한다.

스택 수열이 불가능한 경우를 생각해보면

push 해야할 수가 제시된 숫자보다 더 크고, 제시된 숫자보다 stack에 있는 수가 제시된 수보다 경우이다.

이 경우에는, stack에서 아무리 pop 하더라도 제시된 수를 만들 수 없다.

최종 소스코드

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 fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const n = parseInt(input.shift());
const stack = [];
const answerArr = [];
let isExist = true; // 가능 / 불가능 확인을 위한 flag
let num = 1; // push 해야할 수

for (let j = 0; j < n; j++) {
  if (num <= parseInt(input[j])) {
    // push 해야 할 수가 제시된 수보다 작으면
    // 제시된 수까지 push 한다.
    while (num <= parseInt(input[j])) {
      stack.push(num);
      answerArr.push("+");
      num++;
    }
  } else if (stack[stack.length - 1] > parseInt(input[j])) {
    // push 해야 할 수가 제시된 수보다 크고,
    // stack에 있는 수가 제시된 수보다 크면
    // 아무리 pop해도 스택 수열을 만들 수 없다.
    isExist = false;
    break;
  }

  // 제시된 수까지 push 되었기 때문에,
  // pop 해주면 된다.
  stack.pop();
  answerArr.push("-");
}

if (!isExist) {
  console.log("NO");
} else {
  console.log(answerArr.join("\n"));
}

[백준 문제풀이] 10845번 큐


10845번 큐 (Silver 4)

문제

https://www.acmicpc.net/problem/10845

정수를 저장하는 큐를 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.

명령은 총 여섯 가지이다.

push X: 정수 X를 큐에 넣는 연산이다. pop: 큐에서 가장 앞에 있는 정수를 빼고, 그 수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 -1을 출력한다. size: 큐에 들어있는 정수의 개수를 출력한다. empty: 큐가 비어있으면 1, 아니면 0을 출력한다. front: 큐의 가장 앞에 있는 정수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 -1을 출력한다. back: 큐의 가장 뒤에 있는 정수를 출력한다. 만약 큐에 들어있는 정수가 없는 경우에는 –1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

풀이

배열로 Queue를 만들어서 풀이했다.

첫번째 풀이 소스코드 (시간 초과)

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 fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const N = parseInt(input[0]);
const que = [];
const answerArr = [];

function workQue(cmd) {
  switch (cmd) {
    case "pop":
      answerArr.push(que.length ? que.shift() : -1);
      break;
    case "size":
      answerArr.push(que.length);
      break;
    case "empty":
      answerArr.push(que.length ? 0 : 1);
      break;
    case "front":
      answerArr.push(que.length ? que[0] : -1);
      break;
    case "back":
      answerArr.push(que.length ? que[que.length - 1] : -1);
      break;
    default:
      const line = cmd.split(" ");
      que.push(parseInt(line[1]));
  }
}

for (let i = 1; i <= N; i++) {
  workQue(input[i]);
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 1158번 요세푸스 문제


1158번 요세푸스 문제 (Silver 4)

문제

https://www.acmicpc.net/problem/1158

요세푸스 문제는 다음과 같다.

1번부터 N번까지 N명의 사람이 원을 이루면서 앉아있고, 양의 정수 K(≤ N)가 주어진다. 이제 순서대로 K번째 사람을 제거한다. 한 사람이 제거되면 남은 사람들로 이루어진 원을 따라 이 과정을 계속해 나간다. 이 과정은 N명의 사람이 모두 제거될 때까지 계속된다. 원에서 사람들이 제거되는 순서를 (N, K)-요세푸스 순열이라고 한다. 예를 들어 (7, 3)-요세푸스 순열은 <3, 6, 2, 7, 5, 1, 4>이다.

N과 K가 주어지면 (N, K)-요세푸스 순열을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)

출력

예제와 같이 요세푸스 순열을 출력한다.

풀이

에디터 때와 같이 두 개의 배열을 통해 풀려고 했는데, 하나의 큐를 만들어서 풀 수 있을 것 같았다. N도 5000 이하로 크지 않아서, 시도해보기로 했다.

에디터 문제 보러가기

소스코드

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
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");

const N = parseInt(input[0]);
const K = parseInt(input[1]);

let queue = new Array(N);
queue = queue.fill(0).map((el, index) => (el = index + 1));

let count = 0;
const answerArr = [];

// K번째 있는 사람을 죽이는 함수
// length가 K보다 클 때와 작을 때를 나누어 구현했다.
function kill(cnt) {
  while (cnt !== 0) {
    if (queue.length >= cnt) {
      queue.push(...queue.splice(0, cnt));
      answerArr.push(queue.pop());
      cnt = 0;
    } else {
      cnt -= queue.length;
    }
  }
}

while (count < N) {
  kill(K);
  count++;
}

console.log(`<${answerArr.join(", ")}>`);

사족

다른 사람들의 풀이를 확인해보니, shift를 사용해서 한 사람들은 시간이 더 걸렸고, 배열로 큐나 스택을 직접 구현 하지 않고, index로만 값에 접근 하면 더 빠른 코드를 구현할 수 있었다.


[백준 문제풀이] 1406번 에디터


1406번 에디터 (Silver 2)

문제

https://www.acmicpc.net/problem/1406

한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 600,000글자까지 입력할 수 있다.

이 편집기에는 ‘커서’라는 것이 있는데, 커서는 문장의 맨 앞(첫 번째 문자의 왼쪽), 문장의 맨 뒤(마지막 문자의 오른쪽), 또는 문장 중간 임의의 곳(모든 연속된 두 문자 사이)에 위치할 수 있다. 즉 길이가 L인 문자열이 현재 편집기에 입력되어 있으면, 커서가 위치할 수 있는 곳은 L+1가지 경우가 있다.

이 편집기가 지원하는 명령어는 다음과 같다.

L 커서를 왼쪽으로 한 칸 옮김 (커서가 문장의 맨 앞이면 무시됨) D 커서를 오른쪽으로 한 칸 옮김 (커서가 문장의 맨 뒤이면 무시됨) B 커서 왼쪽에 있는 문자를 삭제함 (커서가 문장의 맨 앞이면 무시됨) 삭제로 인해 커서는 한 칸 왼쪽으로 이동한 것처럼 나타나지만, 실제로 커서의 오른쪽에 있던 문자는 그대로임 P $ $라는 문자를 커서 왼쪽에 추가함 초기에 편집기에 입력되어 있는 문자열이 주어지고, 그 이후 입력한 명령어가 차례로 주어졌을 때, 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 구하는 프로그램을 작성하시오. 단, 명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치하고 있다고 한다.

입력

첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수를 나타내는 정수 M(1 ≤ M ≤ 500,000)이 주어진다. 셋째 줄부터 M개의 줄에 걸쳐 입력할 명령어가 순서대로 주어진다. 명령어는 위의 네 가지 중 하나의 형태로만 주어진다.

출력

첫째 줄에 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 출력한다.

첫번째 풀이

배열로 만든 뒤 cursor의 index를 사용해서 문제를 풀려고 시도했다.

첫번째 풀이 소스코드 (시간 초과)

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
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

let str = input.shift();
str = str.trim().split("");
const N = parseInt(input.shift());
let index = parseInt(str.length > 0 ? str.legnth - 1 : 0);

for (let i = 0; i < N; i++) {
  switch (input[i].trim()) {
    case "L":
      if (index !== 0) {
        index -= 1;
      }
      break;
    case "D":
      if (index !== str.length - 1) {
        index += 1;
      }
      break;
    case "B":
      if (index !== 0) {
        str.splice(index - 1, 1);
      }
      break;
    default:
      const line = input[i].split(" ");
      str.splice(index - 1, 0, line[1]);
  }
}

console.log(str.join(""));

최종 풀이

단순 배열로 풀이하면, 중간에 추가하는 동안의 시간 복잡도가 최대 O(n)으로 시간 초과가 날 수 밖에 없었다.

자료구조로 접근하기로 했다. cursor의 왼쪽과 오른쪽을 배열로 만들어서 오른쪽은 Que, 왼쪽은 Stack의 자료구조로 구현하면 가능할 것이라고 생각했다.

이 경우도 시간 초과가 떠서 찾아보니 pop / push 메서드가 시간복잡도가 O(1)인 것과 달리, shift / unshift 메서드는 배열 맨 앞 요소를 빼야하기 때문에 시간복잡도가 O(n)이어서였다.

이 경우는 두 가지 방법을 생각해볼 수 있었다.

  1. 양쪽 다 스택으로 구현하여 풀이한 뒤, right 배열을 뒤집어서 쓴다.
  2. class를 통해서 유사 배열 객체로 queue를 생성하여서 시간복잡도를 O(1)로 만들어서 푼다.

나는 양쪽 다 스택으로 구현한 뒤 커서 오른쪽의 배열을 뒤집어서 쓰는 방법을 사용했다.

최종 소스코드

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 fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

let str = input.shift();
const N = parseInt(input.shift());
const cursorLeft = str.trim().split("");
let cursorRight = [];

for (let i = 0; i < N; i++) {
  switch (input[i]) {
    case "L":
      if (cursorLeft.length) {
        cursorRight.push(cursorLeft.pop());
      }
      break;
    case "D":
      if (cursorRight.length) {
        cursorLeft.push(cursorRight.pop());
      }
      break;
    case "B":
      if (cursorLeft.length) {
        cursorLeft.pop();
      }
      break;
    default:
      const line = input[i].split(" ");
      cursorLeft.push(line[1]);
  }
}

cursorRight = cursorRight.reverse();

console.log([...cursorLeft, ...cursorRight].join(""));

[백준 문제풀이] 9012번 괄호


9012번 괄호 (Silver 4)

배운 것

조건문에 사용된 문도 실행된 것으로 처리된다. if문에 stack.pop()을 통해서 값이 남아있는지 확인하려고 했었는데, 이 부분에서 배열의 값이 pop 되는 바람에 계속 에러가 났었다.

문제

https://www.acmicpc.net/problem/9012

괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고 부른다. 한 쌍의 괄호 기호로 된 “( )” 문자열은 기본 VPS 이라고 부른다. 만일 x 가 VPS 라면 이것을 하나의 괄호에 넣은 새로운 문자열 “(x)”도 VPS 가 된다. 그리고 두 VPS x 와 y를 접합(concatenation)시킨 새로운 문자열 xy도 VPS 가 된다. 예를 들어 “(())()”와 “((()))” 는 VPS 이지만 “(()(”, “(())()))” , 그리고 “(()” 는 모두 VPS 가 아닌 문자열이다.

여러분은 입력으로 주어진 괄호 문자열이 VPS 인지 아닌지를 판단해서 그 결과를 YES 와 NO 로 나타내어야 한다.

입력

입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 주어진다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 괄호 문자열이 한 줄에 주어진다. 하나의 괄호 문자열의 길이는 2 이상 50 이하이다.

출력

출력은 표준 출력을 사용한다. 만일 입력 괄호 문자열이 올바른 괄호 문자열(VPS)이면 “YES”, 아니면 “NO”를 한 줄에 하나씩 차례대로 출력해야 한다.

풀이

스택을 사용해서 풀 수 있었다.

소스코드

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 fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const T = parseInt(input.shift());
const answerArr = [];

for (let i = 0; i < T; i++) {
  const line = input[i];
  const stack = [];
  let answer = "YES";

  for (let j = 0; j < line.length; j++) {
    if (line[j] === "(") {
      stack.push("(");
    } else {
      if (!stack.pop()) {
        answer = "NO";
        break;
      }
    }
  }
  if (stack.length > 0) {
    answer = "NO";
  }

  answerArr.push(answer);
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 10828번 스택


10828번 스택 (Silver 4)

배운 것

빈 배열에서 pop을 실행하면 에러가 나는 것이 아니라, undefined가 나온다.

&&과   연산자를 활용하면 코드를 간결하게 만들 수 있다. 하지만 많이 사용하면 가독성이 떨어질 수 있다.

문제

https://www.acmicpc.net/problem/10828

정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.

명령은 총 다섯 가지이다.

push X: 정수 X를 스택에 넣는 연산이다. pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다. size: 스택에 들어있는 정수의 개수를 출력한다. empty: 스택이 비어있으면 1, 아니면 0을 출력한다. top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 –1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

스택과 큐

스택 : 택배 상하차 (입출구가 동일) 큐 : 한정판 대기열 (입출구가 다름)

스택은 후입선출 자료구조로 LIFO(Last In First Out)로, 큐는 선입선출 자료구조로 FIFO(First In First Out)이라고도 부른다.

풀이

처음에 문제를 보고 멘붕와서 각 함수를 구현해야하나 생각했었는데, 돌아보니 그럴 필요까지는 없고 그냥 조건문을 사용하면 될 것 같았다. 이 경우 if 문을 사용하는 것보다 switch case 조건문을 사용하는 것이 나을 것 같았다.

이번에는 stack과 알고리즘 관련해서 모르는 것이 많아서 구글에서 나온 블로그를 참고해서 풀이했다.

참고 풀이 : https://gurtn.tistory.com/67 (나를 제외한 천재들)

유튜브, 구글, 다른 스택 풀이들을 보니 결국 js에서 1차원 스택은 여러 상황이나 조건에 따라 구현이 다르겠지만, 당장은 배열을 통한 풀이를 생각할 수 있었다.

소스코드

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
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = input.shift();
const stack = [];
const answerArr = [];

for (let i = 0; i < N; i++) {
  switch (input[i]) {
    case "pop":
      answerArr.push(stack.length ? stack.pop() : -1);
      break;
    case "size":
      answerArr.push(stack.length);
      break;
    case "empty":
      answerArr.push(stack.length ? 0 : 1);
      break;
    case "top":
      answerArr.push(stack.length ? stack[stack.length - 1] : -1);
      break;
    default:
      stack.push(input[i].split(" ")[1]);
  }
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 9093번 단어 뒤집기


9093번 단어 뒤집기 (Bronze 1)

문제

https://www.acmicpc.net/problem/9093

문장이 주어졌을 때, 단어를 모두 뒤집어서 출력하는 프로그램을 작성하시오. 단, 단어의 순서는 바꿀 수 없다. 단어는 영어 알파벳으로만 이루어져 있다.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있으며, 문장이 하나 주어진다. 단어의 길이는 최대 20, 문장의 길이는 최대 1000이다. 단어와 단어 사이에는 공백이 하나 있다.

출력

각 테스트 케이스에 대해서, 입력으로 주어진 문장의 단어를 모두 뒤집어 출력한다.

split + reverse 메서드 풀이

split으로 각 단어를 나눈 배열을 만든 뒤 각각 단어를 뒤집은 배열을 추가해서 공백을 join해서 풀이하면 될 것 같다. 문제는 시간이 가능한가인데, 일단 해보고 다른 섹시한 풀이를 찾아보기로 했다.

소스코드 (split + reverse 메서드 사용, 288ms)

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

const T = input.shift();
const answerArr = [];

for (let i = 0; i < T; i++) {
  const line = input[i].split(" ");
  let reversedLine = [];
  let reversedWord = "";
  for (let l = 0; l < line.length; l++) {
    const reversedWord = line[l].split("").reverse().join("");
    reversedLine.push(reversedWord);
  }
  answerArr.push(reversedLine.join(" "));
}

console.log(answerArr.join(" \n"));

스택을 사용한 풀이

stack 자료 구조의 특징을 활용해서 푸는 경우도 있었다. 문장을 split 하지 않고 stack에 문자를 하나씩 추가하고, 다 추가된 경우 다시 pop을 사용해서 재배열 하는 방식이었다. (524ms)

스택 풀이 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const T = parseInt(input.shift());
const answerArr = [];

for (let i = 0; i < T; i++) {
  let line = input[i];
  let str = "";
  const stack = [];

  for (let j = 0; j <= line.length; j++) {
    if (line[j] === " " || line[j] === undefined) {
      while (stack.length > 0) {
        str += stack.pop();
      }
      str += " ";
    } else {
      stack.push(line[j]);
    }
  }
  answerArr.push(str);
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 18870번 좌표 압축


18870번 좌표 압축 (Silver 2)

문제

https://www.acmicpc.net/problem/18870

수직선 위에 N개의 좌표 X1, X2, …, XN이 있다. 이 좌표에 좌표 압축을 적용하려고 한다.

Xi를 좌표 압축한 결과 X’i의 값은 Xi > Xj를 만족하는 서로 다른 좌표의 개수와 같아야 한다.

X1, X2, …, XN에 좌표 압축을 적용한 결과 X’1, X’2, …, X’N를 출력해보자.

입력

첫째 줄에 N이 주어진다.

둘째 줄에는 공백 한 칸으로 구분된 X1, X2, …, XN이 주어진다.

출력

첫째 줄에 X’1, X’2, …, X’N을 공백 한 칸으로 구분해서 출력한다.

좌표 압축이란?

좌표 압축이란, 정확한 값이 필요 없고 값의 대소관계만이 필요할 때, 모든 수를 0이상 N 미만의 수로 바꾸는 것을 말한다.

첫번째 풀이

N이 1,000,000 이하의 수 인데다가, X는 2 * 10^2만큼의 범위를 가져서 단순 정렬로는 풀지 못할 것이라고 생각했다.

일단 메모리 제한이 512MB인데다가 N이 X보다는 훨씬 적은 범위라서 N만큼의 배열을 만든 뒤, set 객체를 사용해서 함수 중복을 제거한 뒤에 index 값으로 원배열 값을 바꿔주는 것을 생각했다.

첫번째 풀이 소스코드 (시간 초과 - set 객체로 변환 후, index 값으로 변환)

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

const N = parseInt(input.shift());
let numbers = input.toString().trim().split(" ");
const set = new Set(numbers);
const setArr = [...set];
setArr.sort();

const indexArr = new Array(setArr.length);

for (let i = 0; i < N; i++) {
  const index = indexArr.indexOf(numbers[i]);
  numbers[i] = index;
}

console.log(numbers.join(" "));

두번째 풀이

시간 초과가 나올만한 부분이 탐색 부분인 것 같아서 indexOf의 시간복잡도를 찾아보니 O(n)으로 나왔다. n이 최대 1,000,000에 O(n)으로 최대 1,000,000번 탐색해야하니, 시간 초과가 나올 수 밖에 없었다.

이때 사용을 고려 해볼 수 있는 방법으로 객체에 담아서 해시테이블로 사용하는 것과 이진탐색을 사용하는 것이었다. 객체에 담는 forEach의 시간복잡도 또한 O(n)이라서, O(logn)의 시간복잡도를 가지는 이진탐색으로 실행하기로 했다.

두번째 풀이 소스코드 (시간 초과 - )

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 fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = parseInt(input[0]);
let numbers = input[1]
  .toString()
  .split(" ")
  .map((el) => parseInt(el));
const set = new Set(numbers);
let indexArr = [...set];
indexArr.sort((a, b) => a - b);

for (let i = 0; i < N; i++) {
  let low = 0;
  let high = N - 1;
  let mid = Math.floor(high + low / 2);
  while (numbers[i] !== indexArr[mid]) {
    if (numbers[i] < indexArr[mid]) {
      high = mid - 1;
      mid = Math.floor(high + low / 2);
    } else {
      low = mid + 1;
      mid = Math.floor(high + low / 2);
    }
  }
  numbers[i] = mid;
}

console.log(numbers.join(" "));

세번째 풀이

이진 탐색도 결국 최대 1,000,000번 해야했기 때문에, 시간 초과가 나올 수 밖에 없었다.

결국 hash table을 사용하는 방식으로 풀어야 했다. 생각해보니까 처음에 forEach로 hash table을 만들더라도, 마지막 for문에서 찾을 때 시간복잡도가 O(1)인 것이라서 훨-씬 빨랐다.

최종 풀이 소스코드

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

const N = parseInt(input[0]);
let numbers = input[1]
  .toString()
  .split(" ")
  .map((el) => parseInt(el));
const set = new Set(numbers);
let indexArr = [...set];
indexArr.sort((a, b) => a - b);
const indexObj = {};

indexArr.forEach((el, index) => (indexObj[el] = index));

for (let i = 0; i < N; i++) {
  numbers[i] = indexObj[numbers[i]];
}

console.log(numbers.join(" "));

[백준 문제풀이] 1181번 단어 정렬


1181번 단어 정렬 (Silver 5)

문제

https://www.acmicpc.net/problem/1181

알파벳 소문자로 이루어진 N개의 단어가 들어오면 아래와 같은 조건에 따라 정렬하는 프로그램을 작성하시오.

  1. 길이가 짧은 것부터
  2. 길이가 같으면 사전 순으로

입력

첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000) 둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.

출력

조건에 따라 정렬하여 단어들을 출력한다. 단, 같은 단어가 여러 번 입력된 경우에는 한 번씩만 출력한다.

풀이

Set 객체로 만들어서 반복을 지우고, sort를 두 번 사용해서 풀 수 있었다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = input.shift();

let strArr = new Set(input);
strArr = [...strArr];
strArr.sort();

strArr.sort((a, b) => a.length - b.length);

console.log(strArr.join("\n"));

구현할 때 왜 단어순 정렬부터 했을까? (2022.05.04)

문제를 보고 바로 생각해볼 수 있는 것은, 단어 순으로 정렬한 뒤, 길이순으로 정렬하는 것이다.

하지만 조금만 더 생각해보면, 그렇게 정렬하게될 경우 단어순으로 정렬하게되며 길이순 정렬이 모두 꼬인다는 것을 생각해볼 수 있다.


[백준 문제풀이] 10814번 나이순 정렬


10814번 나이순 정렬 (Silver 5)

문제

https://www.acmicpc.net/problem/10814

온라인 저지에 가입한 사람들의 나이와 이름이 가입한 순서대로 주어진다. 이때, 회원들을 나이가 증가하는 순으로, 나이가 같으면 먼저 가입한 사람이 앞에 오는 순서로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 온라인 저지 회원의 수 N이 주어진다. (1 ≤ N ≤ 100,000)

둘째 줄부터 N개의 줄에는 각 회원의 나이와 이름이 공백으로 구분되어 주어진다. 나이는 1보다 크거나 같으며, 200보다 작거나 같은 정수이고, 이름은 알파벳 대소문자로 이루어져 있고, 길이가 100보다 작거나 같은 문자열이다. 입력은 가입한 순서로 주어진다.

출력

첫째 줄부터 총 N개의 줄에 걸쳐 온라인 저지 회원을 나이 순, 나이가 같으면 가입한 순으로 한 줄에 한 명씩 나이와 이름을 공백으로 구분해 출력한다.

풀이

가입순으로 이미 정렬되어있기 때문에, 나이 순으로만 정렬할 수 있도록 sort() 메서드를 사용했다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = input.shift();
input = input.map((el) => el.split(" "));

input.sort((a, b) => parseInt(a[0]) - parseInt(b[0]));

input = input.map((el) => el.join(" "));

console.log(input.join("\n"));

[백준 문제풀이] 좌표 정렬하기 1, 2


11650번, 11651번 좌표 정렬하기 (Silver 5)

두 문제가 작은 차이만 있고, 풀이는 같아서 하나의 게시글에서 다루려고 한다.

11650번 문제

https://www.acmicpc.net/problem/11650

2차원 평면 위의 점 N개가 주어진다. 좌표를 x좌표가 증가하는 순으로, x좌표가 같으면 y좌표가 증가하는 순서로 정렬한 다음 출력하는 프로그램을 작성하시오.

11651번 문제

https://www.acmicpc.net/problem/11651

2차원 평면 위의 점 N개가 주어진다. 좌표를 y좌표가 증가하는 순으로, y좌표가 같으면 x좌표가 증가하는 순서로 정렬한 다음 출력하는 프로그램을 작성하시오.

입력 (동일)

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

출력 (동일)

첫째 줄부터 N개의 줄에 점을 정렬한 결과를 출력한다.

풀이

sort()로 쉽게 풀 수 있었다.

11650번 소스코드

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

const N = parseInt(input.shift());
input = input.map((el) => el.split(" "));

input.sort((a, b) => {
  if (parseInt(a[0]) === parseInt(b[0])) {
    return parseInt(a[1]) - parseInt(b[1]);
  } else {
    return parseInt(a[0]) - parseInt(b[0]);
  }
});

input = input.map((el) => el.join(" "));

console.log(input.join("\n"));

11651번 소스코드

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

const N = parseInt(input.shift());
input = input.map((el) => el.split(" "));

input.sort((a, b) => {
  if (parseInt(a[1]) === parseInt(b[1])) {
    return parseInt(a[0]) - parseInt(b[0]);
  } else {
    return parseInt(a[1]) - parseInt(b[1]);
  }
});

input = input.map((el) => el.join(" "));

console.log(input.join("\n"));

[백준 문제풀이] 10989번 수 정렬하기 3


10989번 수 정렬하기 3 (Silver 5)

문제

https://www.acmicpc.net/problem/10989

N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

시간 제한 5초, 메모리 제한 8MB

접근

힌트가 카운팅 정렬이었어서, 카운팅 정렬에 대해 찾아보았다.

계수 정렬 (Counting sort)

Counting Sort는 O(n)의 시간 복잡도를 가지는 정렬 알고리즘이다. 일반적으로는 평균 시간 복잡도가 O(nlogn)인 Quick Sort 알고리즘이 더 빠르지만 수의 등장 횟수를 누적합으로 바꿔주는 Counting Sort의 경우 수의 범위 특정하게 정해진 경우 range를 구해서 빠르게 정렬할 수 있다. 하지만 대부분의 상황에서 엄청난 메모리 낭비가 될 수 있다.

일단 설명만 보고 스스로 구현을 먼저 해보고, 막히면 찾아보기로 했다.

풀이

범위에 맞는 countArr를 만들어서 풀려고 했는데, 바로 메모리 초과가 나왔다.

찾아보니, node.js는 메모리 초과가 나와서, 추후 제한 완화 예정이라고 해서 파이썬으로 찾아서 문제를 풀었다. 설명되어있는 counting sort를 사용해보고 싶었는데, 생각과는 달리 더 빠른 방법이 있었다.

python으로 풀때도 input으로 받으면 메모리 초과가 나온다고 해서, sys.stdin.readline()을 사용해서 풀었다. 나중에 node.js 메모리 제한이 풀리면 다시 풀어보고 싶다.

파이썬에서는 반복문에 변수가 사용되지 않을 경우, _를 사용한다는 것도 인상깊었다.

그리고 FE 개발자도 파이썬으로 코딩 테스트를 준비하는 이유도 느껴볼 수 있었다…☆

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys

N = int(sys.stdin.readline())
num_list = [0] * 10001

for _ in range(N) :
    num = int(sys.stdin.readline())
    num_list[num] += 1

for i in range(10001) :
    countNum = num_list[i]
    for _ in range(countNum) :
        print(i)

[백준 문제풀이] 2751번 수 정렬하기 2


2751번 수 정렬하기 2 (Silver 5)

문제

https://www.acmicpc.net/problem/2751

N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수가 주어진다. 이 수는 절댓값이 1,000,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

첫번째 풀이

sort 메서드를 사용해 간단하게 풀 수 있었다. 새롭게 알게된 사실은 sort 메서드의 시간 복잡도가 O(n log(n)) 이라는 것이었다.

첫번째 풀이 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require("fs");
const input = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split("\n")
  .map((el) => parseInt(el));

const N = input.shift();

input.sort((a, b) => a - b);

console.log(input.join("\n"));

사족

지금 생각해보면 sort 메서드를 쓰라고 했던 건 아닌 것 같다. 22년 3월 게시글 중에 다른 정렬 알고리즘들을 구현해두었다.


[백준 문제풀이] 1018번 체스판 다시 칠하기


1018번 체스판 다시 칠하기 (Silver 5)

문제

https://www.acmicpc.net/problem/1018

지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M×N 크기의 보드를 찾았다. 어떤 정사각형은 검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 8×8 크기의 체스판으로 만들려고 한다.

체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두 개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이 흰색인 경우, 하나는 검은색인 경우이다.

보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 8×8 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고 생각했다. 당연히 8*8 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

출력

첫째 줄에 지민이가 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.

풀이

체스판을 먼저 만든 다음, 옳은 체스판과 비교해서 브루트 포스로 카운트 한 뒤 가장 작은 수를 찾아서 풀었다.

소스코드

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
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const N = input.shift();
const lines = ["WBWBWBWB", "BWBWBWBW"];
let min = 64;

for (let a = 0; a <= input.length - 8; a++) {
  for (let b = 0; b <= input[a].length - 8; b++) {
    let count = 0;
    for (let k = 0; k < 8; k++) {
      for (let m = 0; m < 8; m++) {
        if (k % 2 === 0) {
          if (input[a + k][b + m] === lines[0][m]) {
            count++;
          }
        } else {
          if (input[a + k][b + m] === lines[1][m]) {
            count++;
          }
        }
      }
    }
    if (count > 32) {
      if (min > 64 - count) min = 64 - count;
    } else {
      if (min > count) min = count;
    }
  }
}

console.log(min);

[백준 문제풀이] 2750번 수 정렬하기


2750번 수 정렬하기 (Bronze 1)

문제

https://www.acmicpc.net/problem/2750

N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수 주어진다. 이 수는 절댓값이 1,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

첫번째 풀이

시간 복잡도가 O(n^2)인 풀이라고 해서, sort 메서드를 이용해서 쉽게 풀었다.

첫번째 풀이 소스코드

1
2
3
4
5
6
7
8
9
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
input = input.map((el) => parseInt(el));

const N = input.shift();

const answerArr = input.sort((a, b) => a - b);

console.log(answerArr.join("\n"));

두번째 풀이

2751번을 풀다가 해당 문제에서는 메서드를 사용하는 것이 아니었다는걸 알고, 다시 버블 정렬로 풀었다.

두번째 풀이 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
input = input.map((el) => parseInt(el));

const N = input.shift();

for (let i = 0; i < input.length; i++) {
  let swap;
  for (let j = 0; j < input.length - 1 - i; j++) {
    if (input[j] > input[j + 1]) {
      swap = input[j + 1];
      input[j + 1] = input[j];
      input[j] = swap;
    }
  }
  if (!swap) {
    break;
  }
}

console.log(input.join("\n"));

[백준 문제풀이] 1436번 영화감독 숌


1436번 영화감독 숌 (Silver 5)

문제

https://www.acmicpc.net/problem/1436

666은 종말을 나타내는 숫자라고 한다. 따라서, 많은 블록버스터 영화에서는 666이 들어간 제목을 많이 사용한다. 영화감독 숌은 세상의 종말 이라는 시리즈 영화의 감독이다. 조지 루카스는 스타워즈를 만들 때, 스타워즈 1, 스타워즈 2, 스타워즈 3, 스타워즈 4, 스타워즈 5, 스타워즈 6과 같이 이름을 지었고, 피터 잭슨은 반지의 제왕을 만들 때, 반지의 제왕 1, 반지의 제왕 2, 반지의 제왕 3과 같이 영화 제목을 지었다.

하지만 숌은 자신이 조지 루카스와 피터 잭슨을 뛰어넘는다는 것을 보여주기 위해서 영화 제목을 좀 다르게 만들기로 했다.

종말의 숫자란 어떤 수에 6이 적어도 3개이상 연속으로 들어가는 수를 말한다. 제일 작은 종말의 숫자는 666이고, 그 다음으로 큰 수는 1666, 2666, 3666, …. 과 같다.

따라서, 숌은 첫 번째 영화의 제목은 세상의 종말 666, 두 번째 영화의 제목은 세상의 종말 1666 이렇게 이름을 지을 것이다. 일반화해서 생각하면, N번째 영화의 제목은 세상의 종말 (N번째로 작은 종말의 숫자) 와 같다.

숌이 만든 N번째 영화의 제목에 들어간 숫자를 출력하는 프로그램을 작성하시오. 숌은 이 시리즈를 항상 차례대로 만들고, 다른 영화는 만들지 않는다.

입력

첫째 줄에 숫자 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.

첫번째 풀이

N이 10000까지 되어서 하나씩 수를 증가하면서 푸는 방식은 섹시하지 못할 것 같지만, 다른 로직이 떠오르지 않아서 일단 수행해보았다.

맞았지만, 시간이 544ms로 뭔가 맘에 들지 않았다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require("fs");
const input = parseInt(fs.readFileSync("/dev/stdin").toString().trim());
let num = 665;
let count = 0;

while (count !== input) {
  num++;
  if (String(num).includes("666")) {
    count++;
  }
}

console.log(num);

사족

조합을 사용해서 자릿수별 666이 포함되는 범위를 찾는 수식을 세웠다. 자릿수 m이 4 이상일 때, (m-3)(m-3)(m-2)*9/2의 범위에 속하게 되는데, 이 범위를 사용해서 m번째 수의 최소 최댓값을 구하면 코드의 효율을 높일 수 있음을 확인했다.


[백준 문제풀이] 11729번 하노이 탑 이동 순서


11729번 하노이 탑 이동 순서 (Silver 1)

문제

https://www.acmicpc.net/problem/11729

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.

  1. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
  2. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.

이 작업을 수행하는데 필요한 이동 순서를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다.

아래 그림은 원판이 5개인 경우의 예시이다.

입력

첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N (1 ≤ N ≤ 20)이 주어진다.

출력

첫째 줄에 옮긴 횟수 K를 출력한다.

두 번째 줄부터 수행 과정을 출력한다. 두 번째 줄부터 K개의 줄에 걸쳐 두 정수 A B를 빈칸을 사이에 두고 출력하는데, 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다.

배운 것

재귀 알고리즘은 선언형 프로그래밍이다.

명령형 프로그래밍은 알고리즘을 명시하고 목표는 명시하지 않는데 반해, 선언형 프로그래밍은 목표를 명시하고 알고리즘을 명시하지 않는 것이다.

즉, 알고리즘에 대해 명시 하는 것이 아니라 목표에 대해 명시하고 알고리즘은 컴퓨터가 해결할 수 있도록 하는 것이 선언형 프로그래밍이다.

목표를 정하고, 종료 조건을 정하고 재귀시켜야한다.

즉, 이 문제에서는 ‘A에서 B로 옮겨라’라는 명령을 추후 일어날 상황까지 고려해서 에러 없는 논리으로 구현하면 되는 것이다.

소스코드

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

const answer = [];
let count = 0;

function movePlate(from, to, other, num) {
  if (num === 0) {
    return;
  } else {
    movePlate(from, other, to, num - 1);
    answer.push(`${from} ${to}`);
    count++;
    movePlate(other, to, from, num - 1);
  }
}

movePlate(1, 3, 2, input);

console.log(count);
console.log(answer.join("\n"));

[백준 문제풀이] 2447번 별 찍기


2447번 별 찍기 (Silver 1)

문제

https://www.acmicpc.net/problem/2447

재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, …)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다.

크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이 하나씩 있는 패턴이다.

N이 3보다 클 경우, 크기 N의 패턴은 공백으로 채워진 가운데의 (N/3)×(N/3) 정사각형을 크기 N/3의 패턴으로 둘러싼 형태이다. 예를 들어 크기 27의 패턴은 예제 출력 1과 같다.

입력

첫째 줄에 N이 주어진다. N은 3의 거듭제곱이다. 즉 어떤 정수 k에 대해 N=3k이며, 이때 1 ≤ k < 8이다.

출력

첫째 줄부터 N번째 줄까지 별을 출력한다.

배운 것

재귀 함수라는 개념 자체가 조금 어려워서 구글 서치를 하고, 문서나 영상들을 찾아봤다. 재귀함수도 결국은 수학적 사고로 추론된 규칙을 기반으로 수행되어야 하는 것이라는걸 이해할 수 있었다.

풀이

별찍기의 공백은 n * n으로 만들어질 때, 좌표가 n % 3 === 1인 지점부터 n / 3까지의 공백이다.

3이 아닌 더 큰 수에서는 결국 3으로 나눠준 몫이 이러한 부분을 가지게 된다. 예를 들어서 Math.floor(4 / 3) = Math.floor(3 / 3)이 된다. 이러한 방식을 사용하면, 3^n인 별을 찍을 수 있다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
const input = parseInt(fs.readFileSync("/dev/stdin").toString().trim());
const answer = [];

for (let i = 0; i < input; i++) {
  for (let j = 0; j < input; j++) {
    makeStar(i, j, input);
  }
  answer.push("\n");
}

function makeStar(x, y, num) {
  if (x % 3 === 1 && y % 3 === 1) {
    answer.push(" ");
  } else {
    if (num === 1) {
      answer.push("*");
    } else {
      makeStar(Math.floor(x / 3), Math.floor(y / 3), num / 3);
    }
  }
}

console.log(answer.join(""));

[백준 문제풀이] 2798번 블랙잭


2798번 블랙잭 (Bronze 2)

문제

https://www.acmicpc.net/problem/2798

카지노에서 제일 인기 있는 게임 블랙잭의 규칙은 상당히 쉽다. 카드의 합이 21을 넘지 않는 한도 내에서, 카드의 합을 최대한 크게 만드는 게임이다. 블랙잭은 카지노마다 다양한 규정이 있다.

한국 최고의 블랙잭 고수 김정인은 새로운 블랙잭 규칙을 만들어 상근, 창영이와 게임하려고 한다.

김정인 버전의 블랙잭에서 각 카드에는 양의 정수가 쓰여 있다. 그 다음, 딜러는 N장의 카드를 모두 숫자가 보이도록 바닥에 놓는다. 그런 후에 딜러는 숫자 M을 크게 외친다.

이제 플레이어는 제한된 시간 안에 N장의 카드 중에서 3장의 카드를 골라야 한다. 블랙잭 변형 게임이기 때문에, 플레이어가 고른 카드의 합은 M을 넘지 않으면서 M과 최대한 가깝게 만들어야 한다.

N장의 카드에 써져 있는 숫자가 주어졌을 때, M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구해 출력하시오.

입력

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는 양의 정수이다.

합이 M을 넘지 않는 카드 3장을 찾을 수 있는 경우만 입력으로 주어진다.

출력

첫째 줄에 M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 출력한다.

배운 것

조합을 위한 알고리즘은 재귀를 사용해서 만든다.

forEach의 parameter는 element, index, array가 있다. element는 각각의 원소이고, index는 해당 원소의 위치, array는 forEach가 진행되는 배열을 말한다.

소스코드

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
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

input[0] = input[0].split(" ");
const N = parseInt(input[0][0]);
const M = parseInt(input[0][1]);
const cards = input[1].split(" ").map((card) => parseInt(card));
let answer = 0;

const getCombinations = (arr, selectNumber) => {
  const results = [];
  if (selectNumber === 1) {
    return arr.map((element) => [element]);
  }

  arr.forEach((fixed, index, origin) => {
    const rest = origin.slice(index + 1);
    const combinations = getCombinations(rest, selectNumber - 1);
    const attached = combinations.map((element) => [fixed, ...element]);
    results.push(...attached);
  });
  return results;
};

getCombinations(cards, 3).forEach((result) => {
  const sum = result[0] + result[1] + result[2];
  if (sum <= M && answer <= sum) {
    answer = sum;
  }
});

console.log(answer);

[백준 문제풀이] 1011번 Fly me to the Alpha Centauri


1011번 Fly me to the Alpha Centauri (Gold 5)

느낀 점

어려운 문제도 계속 생각하면 풀린다.

문제

https://www.acmicpc.net/problem/1011

우현이는 어린 시절, 지구 외의 다른 행성에서도 인류들이 살아갈 수 있는 미래가 오리라 믿었다. 그리고 그가 지구라는 세상에 발을 내려 놓은 지 23년이 지난 지금, 세계 최연소 ASNA 우주 비행사가 되어 새로운 세계에 발을 내려 놓는 영광의 순간을 기다리고 있다.

그가 탑승하게 될 우주선은 Alpha Centauri라는 새로운 인류의 보금자리를 개척하기 위한 대규모 생활 유지 시스템을 탑재하고 있기 때문에, 그 크기와 질량이 엄청난 이유로 최신기술력을 총 동원하여 개발한 공간이동 장치를 탑재하였다. 하지만 이 공간이동 장치는 이동 거리를 급격하게 늘릴 경우 기계에 심각한 결함이 발생하는 단점이 있어서, 이전 작동시기에 k광년을 이동하였을 때는 k-1 , k 혹은 k+1 광년만을 다시 이동할 수 있다. 예를 들어, 이 장치를 처음 작동시킬 경우 -1 , 0 , 1 광년을 이론상 이동할 수 있으나 사실상 음수 혹은 0 거리만큼의 이동은 의미가 없으므로 1 광년을 이동할 수 있으며, 그 다음에는 0 , 1 , 2 광년을 이동할 수 있는 것이다. ( 여기서 다시 2광년을 이동한다면 다음 시기엔 1, 2, 3 광년을 이동할 수 있다. )

김우현은 공간이동 장치 작동시의 에너지 소모가 크다는 점을 잘 알고 있기 때문에 x지점에서 y지점을 향해 최소한의 작동 횟수로 이동하려 한다. 하지만 y지점에 도착해서도 공간 이동장치의 안전성을 위하여 y지점에 도착하기 바로 직전의 이동거리는 반드시 1광년으로 하려 한다.

김우현을 위해 x지점부터 정확히 y지점으로 이동하는데 필요한 공간 이동 장치 작동 횟수의 최솟값을 구하는 프로그램을 작성하라.

입력

입력의 첫 줄에는 테스트케이스의 개수 T가 주어진다. 각각의 테스트 케이스에 대해 현재 위치 x 와 목표 위치 y 가 정수로 주어지며, x는 항상 y보다 작은 값을 갖는다. (0 ≤ x < y < 231)

출력

각 테스트 케이스에 대해 x지점으로부터 y지점까지 정확히 도달하는데 필요한 최소한의 공간이동 장치 작동 횟수를 출력한다.

풀이

y의 최댓값이 2의 31승 –1로 컸기 때문에 수학적으로 접근해야하는 문제라고 생각했다. 고로 규칙성을 찾기 위해 노력했다.

처음에는 오직 1만 이동할 수 있고 마지막에도 오직 1을 이동해야 하기 때문에 이동 count의 최대 이동거리전체 이동 중 1회 이동거리가 가장 긴 수 간의 규칙성을 발견할 수 있었다.

결론적으로 이동 횟수가 홀수 2n-1회일 때는 최대 이동거리를 n^2이며, 짝수 2n회일 때는 n(n+1)라는 식을 유도할 수 있었다.

완전 탐색으로 접근할 경우 시간초과가 뜰 것 같아서, 위의 수식을 사용해서 이동거리가 포함될 최대 이동 횟수 중 홀수와 짝수일 경우를 조건문을 통해 풀이했다.

소스코드

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

const T = parseInt(input[0]);
const answerArr = [];

for (let i = 1; i <= T; i++) {
  const c = input[i].split(" ").map((num) => parseInt(num));
  const distance = c[1] - c[0];
  const max = Math.ceil(Math.sqrt(distance));
  if (distance <= max * (max - 1)) {
    answerArr.push(max * 2 - 2);
  } else {
    answerArr.push(max * 2 - 1);
  }
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 10870번 피보나치 수 5


10870번 피보나치 수 5 (Bronze 2)

문제

https://www.acmicpc.net/problem/10870

피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다.

이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n ≥ 2)가 된다.

n=17일때 까지 피보나치 수를 써보면 다음과 같다.

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597

n이 주어졌을 때, n번째 피보나치 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 n이 주어진다. n은 20보다 작거나 같은 자연수 또는 0이다.

출력

첫째 줄에 n번째 피보나치 수를 출력한다.

풀이

꼬리 재귀로 풀이했다.

소스코드

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

function fibonacci(n, number = 0, total = 1) {
  if (n === 0) {
    return 0;
  } else if (n === 1) {
    return total;
  } else {
    return fibonacci(n - 1, total, number + total);
  }
}

const answer = fibonacci(input);

console.log(answer);

[백준 문제풀이] 10872번 팩토리얼


10872번 팩토리얼 (Bronze 3)

문제

https://www.acmicpc.net/problem/10872

0보다 크거나 같은 정수 N이 주어진다. 이때, N!을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 정수 N(0 ≤ N ≤ 12)이 주어진다.

출력

첫째 줄에 N!을 출력한다.


재귀함수란?

재귀함수란 함수에서 스스로를 호출해서 명령을 수행하는 함수이다. 반복문을 사용하는 명령은 모두 재귀 함수를 통해 구현이 가능하고, 재귀함수를 반복문으로 만드는 것도 가능하다.

재귀함수를 만들 때는, 재귀 호출을 그만둘 수 있는 조건을 만들어야 무한 루프를 막을 수 있다.

재귀 함수는 단순히 명령을 반복하는 것이 아니라, 함수를 반복해서 호출하므로 메모리를 많이 차지하기 때문에 반복문에 비해 느리다.

그럼에도 재귀함수를 사용하는 이유는 변수를 만들 필요가 없으며, 반복문을 사용하지 않기 때문에 코드가 매우 간결해지기 때문이다.

다만 위에서 말한대로 단순한 반복문이 아니라 반복적으로 함수를 호출하기 때문에, 해당 함수에서 필요한 것들을 메모리에 저장하는 과정에서 스택 메모리가 커지고, stack overflow가 발생할 수 있다.

이러한 에러를 방지하기 위해 사용하는 방법이 ‘꼬리 재귀 최적화’ 이다.

꼬리 재귀는 재귀 함수의 호출 이후 바로 결과만 반환할 수 있도록 하는 방법이다. 이러한 것은 코드에서는 바로 볼 수 없고, 자바스크립트 언어에서 ‘꼬리 재귀 최적화’라는 기능을 지원해주기 때문이다. 이러한 이유로, 꼬리 재귀에서 return 값은 마지막(tail - 꼬리)부분에 있어야한다.

재귀 함수 예시

1
2
3
4
5
6
function factorial(n) {
  if (n === 1) {
    return total;
  }
  return n * factorial(n - 1);
}

꼬리 재귀 예시

1
2
3
4
5
6
function factorial(n, total = 1) {
  if (n === 1) {
    return total;
  }
  return factorial(n - 1, n * total);
}

출처: https://joooing.tistory.com/entry/재귀-→-꼬리-재귀-Tail-Recursion [joooing]

둘이 뭐가 다른 걸까?

위에서 설명된 일반 재귀는 값을 받으면, 그 값에 연산을 하고 다시 재귀 함수에 전달해줬다. 하지만 꼬리 재귀는 아무것도 하지 않고 인수로 total 값을 전달만 해준다. 결과 값에 아무 일도 하지 않고 다시 반환하기 때문에, 스택 오버플로우가 발생하지 않게 되는 방식이다.

콜스택과 스택 오버플로우, 꼬리재귀 최적화

자바스크립트는 단일 스레드 프로그래밍 언어이다. 이 말은 쉽게 말하자면, 자바스크립트라는 언어는 안에 들어있는 계산 요정님이 한 명에다가 멀티태스킹은 할 줄 모른다라는 것이다.

그렇기 때문에 콜스택이라는 실행 대기열을 만들고, 하나씩 대기열에서 명령을 꺼내서 처리해준다.

근데 이 요정님께도 역량과 나름의 사정이 존재하기 때문에 우리가 요청할 수 있는 대기열의 최대 길이는 13939개 정도이다.

그래서 많은 양의 함수가 있을 때 요정님께

첫 번째로 값을 받으시구요. 두 번째로 이 값을 곱해서 세 번째로 아까 이 값 드렸던 위치로 전송 부탁드려요.

같이 여러 일로 내용을 나눠서 요청을 하게 되면, 요정님은 파업해버리신다. 이 파업선언이 일반 재귀에서 마주할 수 있는 Uncaught RangeError: Maximum call stack size exceeded이다.

그렇기 때문에 꼬리재귀를 사용해서 요정님이 할 일을 간단 명료하게 줄여주는 것이다. 꼬리재귀를 사용하게 되면

다음 함수 부르실 때, 곱해진 이 값좀 같이 넘겨주세요.

라는 간략화된 요청으로 말할 수 있게된다.


풀이

꼬리 재귀를 구현해서 풀었다. 재귀는 아직 너무 어렵다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fs = require("fs");

const input = parseInt(fs.readFileSync("/dev/stdin").toString().trim());

function factorial(n, total = 1) {
  if (n === 0 || n === 1) {
    return total;
  } else {
    return factorial(n - 1, n * total);
  }
}

const answer = factorial(input);

console.log(answer);

[백준 문제풀이] 1002번 터렛


1002번 터렛 (Silver 4)

문제

https://www.acmicpc.net/problem/1002

조규현과 백승환은 터렛에 근무하는 직원이다. 하지만 워낙 존재감이 없어서 인구수는 차지하지 않는다. 다음은 조규현과 백승환의 사진이다.

이석원은 조규현과 백승환에게 상대편 마린(류재명)의 위치를 계산하라는 명령을 내렸다. 조규현과 백승환은 각각 자신의 터렛 위치에서 현재 적까지의 거리를 계산했다.

조규현의 좌표 (x1, y1)와 백승환의 좌표 (x2, y2)가 주어지고, 조규현이 계산한 류재명과의 거리 r1과 백승환이 계산한 류재명과의 거리 r2가 주어졌을 때, 류재명이 있을 수 있는 좌표의 수를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 다음과 같이 이루어져 있다.

한 줄에 x1, y1, r1, x2, y2, r2가 주어진다. x1, y1, x2, y2는 -10,000보다 크거나 같고, 10,000보다 작거나 같은 정수이고, r1, r2는 10,000보다 작거나 같은 자연수이다.

출력

각 테스트 케이스마다 류재명이 있을 수 있는 위치의 수를 출력한다. 만약 류재명이 있을 수 있는 위치의 개수가 무한대일 경우에는 –1을 출력한다.

풀이

각 터렛의 위치가 [같음, 다름] 각각의 경우에서 원이 [내접할 때, 외접할 때, 겹칠 때, 반지름이 같을 때]로 나눠서 쉽게 풀 수 있었다.

소스코드

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const T = parseInt(input[0]);
const answerArr = [];

function checkCount(numArr) {
  const xFir = numArr[0];
  const yFir = numArr[1];
  const rFir = numArr[2];
  const xSec = numArr[3];
  const ySec = numArr[4];
  const rSec = numArr[5];
  const max = Math.max(rFir, rSec);
  const min = Math.min(rFir, rSec);

  const distance = Math.sqrt(
    Math.pow(xFir - xSec, 2) + Math.pow(yFir - ySec, 2)
  );
  if (xFir === xSec && yFir === ySec) {
    if (rFir === rSec) {
      answerArr.push(-1);
      return;
    } else {
      answerArr.push(0);
      return;
    }
  } else {
    if (distance === max) {
      answerArr.push(2);
      return;
    } else if (distance < max) {
      if (distance + min === max) {
        answerArr.push(1);
        return;
      } else if (distance + min < max) {
        answerArr.push(0);
        return;
      } else {
        answerArr.push(2);
        return;
      }
    } else {
      if (distance === max + min) {
        answerArr.push(1);
        return;
      } else if (distance > max + min) {
        answerArr.push(0);
        return;
      } else {
        answerArr.push(2);
        return;
      }
    }
  }
}

for (let i = 1; i <= T; i++) {
  const numbers = input[i].split(" ").map((num) => parseInt(num));
  checkCount(numbers);
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 3053번 택시 기하학


3053번 택시 기하학 (Bronze 3)

문제

https://www.acmicpc.net/problem/3053

19세기 독일 수학자 헤르만 민코프스키는 비유클리드 기하학 중 택시 기하학을 고안했다.

택시 기하학에서 두 점 T1(x1,y1), T2(x2,y2) 사이의 거리는 다음과 같이 구할 수 있다.

D(T1,T2) = x1-x2 + y1-y2

두 점 사이의 거리를 제외한 나머지 정의는 유클리드 기하학에서의 정의와 같다.

따라서 택시 기하학에서 원의 정의는 유클리드 기하학에서 원의 정의와 같다.

원: 평면 상의 어떤 점에서 거리가 일정한 점들의 집합

반지름 R이 주어졌을 때, 유클리드 기하학에서 원의 넓이와, 택시 기하학에서 원의 넓이를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 반지름 R이 주어진다. R은 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄에는 유클리드 기하학에서 반지름이 R인 원의 넓이를, 둘째 줄에는 택시 기하학에서 반지름이 R인 원의 넓이를 출력한다. 정답과의 오차는 0.0001까지 허용한다.

풀이

택시 기하학의 개념을 이해하는게 푸는 것보다 오래 걸렸다. 결국 내용을 이해해보면, 택시 기하학에서는 원의 넓이가 2*r^2 였다.


[백준 문제풀이] 4153번 직각삼각형


4153번 직각삼각형 (Bronze 3)

문제

https://www.acmicpc.net/problem/4153

과거 이집트인들은 각 변들의 길이가 3, 4, 5인 삼각형이 직각 삼각형인것을 알아냈다. 주어진 세변의 길이로 삼각형이 직각인지 아닌지 구분하시오.

입력

입력은 여러개의 테스트케이스로 주어지며 마지막줄에는 0 0 0이 입력된다. 각 테스트케이스는 모두 30,000보다 작은 양의 정수로 주어지며, 각 입력은 변의 길이를 의미한다.

출력

각 입력에 대해 직각 삼각형이 맞다면 “right”, 아니라면 “wrong”을 출력한다.

풀이

피타고라스의 정리를 이용해서 풀 수 있었다. 나는 최댓값을 구해 빗변으로 삼은 뒤 배열에서 제거해서 했는데, 다른 사람들의 풀이를 보니 정렬을 사용하면 더 효율적으로 할 수 있다는 걸 배웠다.

소스코드 (피타고라스의 정리)

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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const answerArr = [];
let count = 0;

function isRight(triangle) {
  answerArr.push("wrong");
  const hypo = Math.max(...triangle);
  const index = triangle.indexOf(hypo);
  triangle.splice(index, 1);
  if (
    Math.pow(hypo, 2) ===
    Math.pow(triangle[0], 2) + Math.pow(triangle[1], 2)
  ) {
    answerArr[count] = "right";
  }
}

while (parseInt(input[count][0]) !== 0) {
  const tri = input[count].split(" ").map((num) => parseInt(num));
  isRight(tri);
  count++;
}

console.log(answerArr.join("\n"));

[백준 문제풀이] 9020번 골드바흐의 추측


9020번 골드바흐의 추측 (Silver 1)

문제

https://www.acmicpc.net/problem/9020

1보다 큰 자연수 중에서 1과 자기 자신을 제외한 약수가 없는 자연수를 소수라고 한다. 예를 들어, 5는 1과 5를 제외한 약수가 없기 때문에 소수이다. 하지만, 6은 6 = 2 × 3 이기 때문에 소수가 아니다.

골드바흐의 추측은 유명한 정수론의 미해결 문제로, 2보다 큰 모든 짝수는 두 소수의 합으로 나타낼 수 있다는 것이다. 이러한 수를 골드바흐 수라고 한다. 또, 짝수를 두 소수의 합으로 나타내는 표현을 그 수의 골드바흐 파티션이라고 한다. 예를 들면, 4 = 2 + 2, 6 = 3 + 3, 8 = 3 + 5, 10 = 5 + 5, 12 = 5 + 7, 14 = 3 + 11, 14 = 7 + 7이다. 10000보다 작거나 같은 모든 짝수 n에 대한 골드바흐 파티션은 존재한다.

2보다 큰 짝수 n이 주어졌을 때, n의 골드바흐 파티션을 출력하는 프로그램을 작성하시오. 만약 가능한 n의 골드바흐 파티션이 여러 가지인 경우에는 두 소수의 차이가 가장 작은 것을 출력한다.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고 짝수 n이 주어진다.

출력

각 테스트 케이스에 대해서, n보다 크고, 2n보다 작거나 같은 소수의 개수를 출력한다.

풀이

각 테스트 케이스에 대해서 주어진 n의 골드바흐 파티션을 출력한다. 출력하는 소수는 작은 것부터 먼저 출력하며, 공백으로 구분한다.

소스코드

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
40
41
42
43
44
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const T = parseInt(input[0]);
const answerArr = [];

for (let i = 1; i <= T; i++) {
  const number = parseInt(input[i]);
  let a = number / 2;
  let b = number - a;

  while (true) {
    let isPrime = true;
    if (a % 2 === 0 || b % 2 === 0) {
      if (!(a === 2 && b === 2)) {
        isPrime = false;
      }
    } else {
      for (let m = 3; m <= Math.sqrt(a); m += 2) {
        if (a % m === 0) {
          isPrime = false;
          break;
        }
      }
      if (isPrime) {
        for (let n = 3; n <= Math.sqrt(b); n += 2) {
          if (b % n === 0) {
            isPrime = false;
            break;
          }
        }
      }
    }
    if (isPrime) {
      answerArr.push(`${a} ${b}`);
      break;
    } else {
      a -= 1;
      b += 1;
    }
  }
}

console.log(answerArr.join("\n"));

사족 (2022.04.26)

이 당시에는 소수를 구하는 방식으로 풀었는데, 지금 돌아보면 에라토스테네스의 체로 접근하는 것이 더 나은 풀이 같다.


[백준 문제풀이] 3009번 네 번째 점


3009번 네 번째 점 (Bronze 3)

문제

https://www.acmicpc.net/problem/3009

세 점이 주어졌을 때, 축에 평행한 직사각형을 만들기 위해서 필요한 네 번째 점을 찾는 프로그램을 작성하시오.

입력

세 점의 좌표가 한 줄에 하나씩 주어진다. 좌표는 1보다 크거나 같고, 1000보다 작거나 같은 정수이다.

출력

직사각형의 네 번째 점의 좌표를 출력한다.

제한
1 ≤ w, h ≤ 1,000
1 ≤ x ≤ w-1
1 ≤ y ≤ h-1
x, y, w, h는 정수

풀이

직사각형을 만들기 위해 입력에 주어지는 x, y 값들이 결국 두 번씩 반복되어야 한다. x, y 각각 배열을 만들어서 풀었다.

소스코드

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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const a = input[0].split(" ");
const b = input[1].split(" ");
const c = input[2].split(" ");

const line = [a, b, c];

const xArr = [];
const yArr = [];

let x;
let y;

for (let i = 0; i <= 2; i++) {
  xArr.push(parseInt(line[i][0]));
  yArr.push(parseInt(line[i][1]));
}

for (let l = 0; l <= 2; l++) {
  const xDot = parseInt(line[l][0]);
  const yDot = parseInt(line[l][1]);
  if (xArr.indexOf(xDot) === xArr.lastIndexOf(xDot)) {
    x = xDot;
  }
  if (yArr.indexOf(yDot) === yArr.lastIndexOf(yDot)) {
    y = yDot;
  }
}

console.log(`${x} ${y}`);

[백준 문제풀이] 1085번 직사각형에서 탈출


1085번 직사각형에서 탈출 (Bronze 3)

문제

https://www.acmicpc.net/problem/1085

한수는 지금 (x, y)에 있다. 직사각형은 각 변이 좌표축에 평행하고, 왼쪽 아래 꼭짓점은 (0, 0), 오른쪽 위 꼭짓점은 (w, h)에 있다. 직사각형의 경계선까지 가는 거리의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 x, y, w, h가 주어진다.

출력

첫째 줄에 문제의 정답을 출력한다.

제한
1 ≤ w, h ≤ 1,000
1 ≤ x ≤ w-1
1 ≤ y ≤ h-1
x, y, w, h는 정수

풀이

x, y, w, h, 0, 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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");

const x = parseInt(input[0]);
const y = parseInt(input[1]);
const w = parseInt(input[2]);
const h = parseInt(input[3]);

let min;

if (w - x < x) {
  min = w - x;
} else {
  min = x;
}

if (h - y < y) {
  if (h - y < min) {
    min = h - y;
  }
} else if (min > y) {
  min = y;
}

console.log(min);

[백준 문제풀이] 4948번 베르트랑 공준


4948번 베르트랑 공준 (Silver 2)

문제

https://www.acmicpc.net/problem/4948

베르트랑 공준은 임의의 자연수 n에 대하여, n보다 크고, 2n보다 작거나 같은 소수는 적어도 하나 존재한다는 내용을 담고 있다.

이 명제는 조제프 베르트랑이 1845년에 추측했고, 파프누티 체비쇼프가 1850년에 증명했다.

예를 들어, 10보다 크고, 20보다 작거나 같은 소수는 4개가 있다. (11, 13, 17, 19) 또, 14보다 크고, 28보다 작거나 같은 소수는 3개가 있다. (17,19, 23)

자연수 n이 주어졌을 때, n보다 크고, 2n보다 작거나 같은 소수의 개수를 구하는 프로그램을 작성하시오.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 케이스는 n을 포함하는 한 줄로 이루어져 있다.

입력의 마지막에는 0이 주어진다.

출력

각 테스트 케이스에 대해서, n보다 크고, 2n보다 작거나 같은 소수의 개수를 출력한다.

풀이

반복문과 제곱근 나누기 방식의 에라토노스체로 접근했다.

소스코드

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
const fs = require("fs");

const input = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split("\n")
  .map((el) => el * 1);

const max = Math.max(...input);
let screen = Array.from({ length: max * 2 + 1 }, () => true);
screen[0] = false;
screen[1] = false;

for (let i = 2; i <= max * 2; i++) {
  if (screen[i]) {
    for (let j = 2; i * j <= max * 2; j++) {
      screen[i * j] = false;
    }
  }
}

let answer = Array.from({ length: input.length - 1 }, () => 0);
for (let i = 0; i < input.length - 1; i++) {
  for (let j = input[i] + 1; j <= input[i] * 2; j++) {
    if (screen[j]) answer[i] += 1;
  }
}

console.log(answer.join("\n"));

[백준 문제풀이] 1929번 소수 구하기


1929번 소수 구하기 (Silver 3)

문제

https://www.acmicpc.net/problem/1929

M이상 N이하의 소수를 모두 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 M과 N이 빈 칸을 사이에 두고 주어진다. (1 ≤ M ≤ N ≤ 1,000,000) M이상 N이하의 소수가 하나 이상 있는 입력만 주어진다.

출력

한 줄에 하나씩, 증가하는 순서대로 소수를 출력한다.

시간 제한 : 2초, 메모리 제한 : 256mb

첫 번째 풀이

이번이야말로 에라토스테네스의 체를 사용해야하나 싶었는데, 1,000,000까지 배수의 배열을 만드는 것이 비효율적일 것 같았다. 해서 제곱근까지의 곱을 비교하는 로직을 사용했다.

수가 커서 이전에 사용했던 직접 나누어서 계산하는 로직을 사용하면, 시간 초과가 생길 것 같았다.

직접 나누는 로직의 시간 복잡도는 O(N)이고, 제곱근까지의 수를 나누어서 계산하는 것은 O(√ N)로, 앞의 방법보다 빠르다.

소스코드

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
const fs = require("fs");
const [M, N] = fs
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split(" ")
  .map((el) => el * 1);

let answer = "";
let screen = Array.from({ length: N + 1 }, () => true);
screen[0] = false;
screen[1] = false;

for (let i = 2; i <= Math.floor(Math.sqrt(N)); i++) {
  if (screen[i]) {
    for (let j = 2; i * j <= N; j++) {
      screen[i * j] = false;
    }
  }
}

for (let i = M; i <= N; i++) {
  if (screen[i]) answer += i + "\n";
}

console.log(answer);

[백준 문제풀이] 11653번 소인수분해


11653번 소인수분해 (Silver 5)

문제

https://www.acmicpc.net/problem/11653

정수 N이 주어졌을 때, 소인수분해하는 프로그램을 작성하시오.

입력

첫째 줄에 정수 N (1 ≤ N ≤ 10,000,000)이 주어진다.

출력

N의 소인수분해 결과를 한 줄에 하나씩 오름차순으로 출력한다. N이 1인 경우 아무것도 출력하지 않는다.

제한시간 : 1초

첫 번째 풀이

N이 10,000,000이하라서, 전부 일일이 나눠보는 것은 문제가 있다고 생각했다. 소인수분해라는 것은 결국 소수로 나눠지는 것이기 때문에, N까지의 소수를 먼저 구한 뒤 하나씩 나눠보는 방식이 맞다고 생각했다.

아니었다.

두 번째 풀이

작은 수부터 하나씩 나눠보면, 자연스럽게 소인수분해가 된다는 것을 알았다. 다만 나눠지지 않을때는 수를 하나씩 올리면서 실행해보았다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require("fs");

let input = parseInt(fs.readFileSync("/dev/stdin").toString().trim());
const primes = [];

let divider = 2;
while (input >= divider) {
  if (input % divider === 0) {
    input = input / divider;
    primes.push(divider);
  } else {
    divider++;
  }
}

console.log(primes.join("\n"));

[백준 문제풀이] 2581번 소수


2581번 소수 (Silver 5)

문제

https://www.acmicpc.net/problem/2581

자연수 M과 N이 주어질 때 M이상 N이하의 자연수 중 소수인 것을 모두 골라 이들 소수의 합과 최솟값을 찾는 프로그램을 작성하시오.

예를 들어 M=60, N=100인 경우 60이상 100이하의 자연수 중 소수는 61, 67, 71, 73, 79, 83, 89, 97 총 8개가 있으므로, 이들 소수의 합은 620이고, 최솟값은 61이 된다.

출력

M이상 N이하의 자연수 중 소수인 것을 모두 찾아 첫째 줄에 그 합을, 둘째 줄에 그 중 최솟값을 출력한다.

단, M이상 N이하의 자연수 중 소수가 없을 경우는 첫째 줄에 -1을 출력한다.

제한시간 : 1초

풀이

M과 N이 10,000 이하의 자연수라서, 에라토스테네스의 체를 사용해야하나 생각이 들었다. 문제는 메모리가 128mb 라는 것이었다.

그냥 나누기로 했더니 풀렸다. 자꾸 오류가 났었는데, 배열을 Boolean 값으로 검증하는 방식보다 배열의 길이가 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 fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const a = parseInt(input[0]);
const b = parseInt(input[1]);
const primeNumbers = [];
let sum = 0;

for (let i = a; i <= b; i++) {
  if (i === 2) {
    primeNumbers.push(i);
    sum += i;
  } else if (i !== 1) {
    let multiple = false;
    for (let l = 2; l < i; l++) {
      if (i % l === 0) {
        multiple = true;
      }
    }
    if (!multiple) {
      primeNumbers.push(i);
      sum += i;
    }
  }
}

if (!primeNumbers.length) {
  console.log(-1);
} else {
  const min = Math.min(...primeNumbers);
  console.log(sum);
  console.log(min);
}

[백준 문제풀이] 1978번 소수 찾기


1978번 소수 찾기 (Silver 4)

문제

https://www.acmicpc.net/problem/1978

주어진 수 N개 중에서 소수가 몇 개인지 찾아서 출력하는 프로그램을 작성하시오.

입력

첫 줄에 수의 개수 N이 주어진다. N은 100이하이다. 다음으로 N개의 수가 주어지는데, 수는 1000 이하의 자연수이다.

풀이 : 에라토스테네스의 체

소수를 구하는 공식이 있을 것 같아서 서치해보았고, 에라토스테네스의 체라는 것을 알게 되었다. 에라토스테네스의 체는 1이 아닌 수의 배수들을 제외한 값들을 소수로 판단하여 계산하는 것으로, 모든 수를 그 이하의 다른 수로 나누는 것보다 효율적이라고 한다.

소스코드

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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = parseInt(input[0]);
const numbers = input[1].trim().split(" ");
const nonMultiples = [];

numbers.map((number) => {
  let multiple = false;

  if (parseInt(number) === 1) {
    multiple = true;
  }

  for (let l = 2; l < parseInt(number); l++) {
    if (parseInt(number) % l === 0) {
      multiple = true;
    }
  }

  if (parseInt(number) === 2) {
    nonMultiples.push(number);
  } else if (!multiple) {
    nonMultiples.push(number);
  }
});

if (numbers === []) {
  console.log(0);
} else {
  console.log(nonMultiples.length);
}

[백준 문제풀이] 2839번 설탕 배달


2839번 설탕 배달 (Bronze 1)

문제

https://www.acmicpc.net/problem/2839

상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그램 봉지와 5킬로그램 봉지가 있다.

상근이는 귀찮기 때문에, 최대한 적은 봉지를 들고 가려고 한다. 예를 들어, 18킬로그램 설탕을 배달해야 할 때, 3킬로그램 봉지 6개를 가져가도 되지만, 5킬로그램 3개와 3킬로그램 1개를 배달하면, 더 적은 개수의 봉지를 배달할 수 있다.

상근이가 설탕을 정확하게 N킬로그램 배달해야 할 때, 봉지 몇 개를 가져가면 되는지 그 수를 구하는 프로그램을 작성하시오.

첫째 줄에 N이 주어진다. (3 ≤ N ≤ 5000)

상근이가 배달하는 봉지의 최소 개수를 출력한다. 만약, 정확하게 N킬로그램을 만들 수 없다면 -1을 출력한다.

풀이

N이 5000까지이기 때문에 단순 반복 계산은 아닐거라고 생각했다.

! 아이디어
나머지와 몫을 사용하면 간단히 풀 수 있지 않을까? 5키로그램 봉지를 최대한 많이 가져가면 가져갈 봉지가 줄어든다.

3의 봉지와 5의 봉지를 반복문 없이 계산할 수 있지 않을까 했는데 어려웠다.

5로 나누어서 나머지가 0이 된다면 제일 적은 봉지 수이기 때문에 반복문에 첫 부분에 조건문으로 넣어주고, 총 무게에서 3을 계속 빼어서 count를 통해 3키로 봉지를 세어서 남은 무게를 5로 나눈 것과 더해주었다.

나중에 알고보니 이게 그리디 알고리즘이었다더라.


[백준 문제풀이] 2775번 부녀회장이 될테야


2775번 부녀회장이 될테야 (Bronze 1)

문제

https://www.acmicpc.net/problem/2775

평소 반상회에 참석하는 것을 좋아하는 주희는 이번 기회에 부녀회장이 되고 싶어 각 층의 사람들을 불러 모아 반상회를 주최하려고 한다.

이 아파트에 거주를 하려면 조건이 있는데, “a층의 b호에 살려면 자신의 아래(a-1)층의 1호부터 b호까지 사람들의 수의 합만큼 사람들을 데려와 살아야 한다” 는 계약 조항을 꼭 지키고 들어와야 한다.

아파트에 비어있는 집은 없고 모든 거주민들이 이 계약 조건을 지키고 왔다고 가정했을 때, 주어지는 양의 정수 k와 n에 대해 k층에 n호에는 몇 명이 살고 있는지 출력하라. 단, 아파트에는 0층부터 있고 각층에는 1호부터 있으며, 0층의 i호에는 i명이 산다.

제한 시간 : 1초

제한 : 1 ≤ k, n ≤ 14

첫 번째 풀이

제한과 시간을 봤을 때, 각 층이 올라갈때마다 바로 아래층과 이전 방의 값을 더해서 배열로 만들어주는 것으로 구현이 가능할 수 있겠다고 생각했다.

틀림.

두 번째 풀이

자꾸 에러가 나서 2일 동안 고민했는데, 알고보니까 사례 수는 층, 호수를 전부 포함하는 것이라서 반복문을 T번 더 해주어야했다. 어썸 코딩… (2021.11.12.)

풀이 이후 자꾸 에러가 나서 서치를 해봤는데, 이 문제의 아래에 전부 1로된 층을 추가한 뒤, 파스칼의 삼각형으로 접근, 이항정리 문제로 푸는 방법을 찾았다. 오늘도 어썸 코딩.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const T = parseInt(input[0]);

for (let i = 1; i < T * 2; i = i + 2) {
  const k = parseInt(input[i]);
  const n = parseInt(input[i + 1]);
  const apartment = [];

  for (let l = 0; l <= k; l++) {
    apartment.push([1]);
    for (let j = 1; j < n; j++) {
      if (l === 0) {
        apartment[l].push(j + 1);
      } else {
        apartment[l].push(apartment[l - 1][j] + apartment[l][j - 1]);
      }
    }
  }
  console.log(apartment[k][n - 1]);
}

[백준 문제풀이] 10757번 큰 수 A + B


10757번 큰 수 A + B (Bronze 5)

배운 것

문제는 간단했는데, js에서 처리할 수 있는 수에 한계가 있어서 BigInt라는 자료형이 있다는 것을 알게 되었다. BigInt는 끝에 n이 붙기 때문에 string으로 변환해서 출력해주어야한다.

문제

https://www.acmicpc.net/problem/10757

두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.


[백준 문제풀이] 2869번 달팽이는 올라가고 싶다


2869번 달팽이는 올라가고 싶다 (Bronze 1)

문제

https://www.acmicpc.net/problem/2869

땅 위에 달팽이가 있다. 이 달팽이는 높이가 V미터인 나무 막대를 올라갈 것이다.

달팽이는 낮에 A미터 올라갈 수 있다. 하지만, 밤에 잠을 자는 동안 B미터 미끄러진다. 또, 정상에 올라간 후에는 미끄러지지 않는다.

달팽이가 나무 막대를 모두 올라가려면, 며칠이 걸리는지 구하는 프로그램을 작성하시오.

첫째 줄에 세 정수 A, B, V가 공백으로 구분되어서 주어진다. (1 ≤ B < A ≤ V ≤ 1,000,000,000)

제한시간 : 0.15초

첫 번째 풀이

수가 크기 때문에 반복문을 사용하는 것은 불가능하고, 나눠서 해결하는 방식이라고 생각했다. 다음 날 낮에 올라가는 것까지 계산해야하기 때문에, 미리 낮에 올라가는걸 뺀 후 그 수를 넘는 때를 구하기로 했다.

25%에서 틀림.

두 번째 풀이

25%에서 틀렸다는 것은 예외처리가 잘 되지 못한 것 같다.

floor를 사용해서 문제를 풀었었는데, v – a의 값이 a보다 작으면 반내림을 사용하게 되면 오답이 나오게 되기 때문에 반올림을 사용해서 문제를 풀어야했다.


[백준 문제풀이] 10250번 ACM 호텔


10250번 ACM 호텔 (Bronze 3)

문제

https://www.acmicpc.net/problem/10250

ACM 호텔 매니저 지우는 손님이 도착하는 대로 빈 방을 배정하고 있다. 고객 설문조사에 따르면 손님들은 호텔 정문으로부터 걸어서 가장 짧은 거리에 있는 방을 선호한다고 한다. 여러분은 지우를 도와 줄 프로그램을 작성하고자 한다. 즉 설문조사 결과 대로 호텔 정문으로부터 걷는 거리가 가장 짧도록 방을 배정하는 프로그램을 작성하고자 한다.

문제를 단순화하기 위해서 호텔은 직사각형 모양이라고 가정하자. 각 층에 W 개의 방이 있는 H 층 건물이라고 가정하자 (1 ≤ H, W ≤ 99). 그리고 엘리베이터는 가장 왼쪽에 있다고 가정하자(그림 1 참고). 이런 형태의 호텔을 H × W 형태 호텔이라고 부른다. 호텔 정문은 일층 엘리베이터 바로 앞에 있는데, 정문에서 엘리베이터까지의 거리는 무시한다. 또 모든 인접한 두 방 사이의 거리는 같은 거리(거리 1)라고 가정하고 호텔의 정면 쪽에만 방이 있다고 가정한다.

방 번호는 YXX 나 YYXX 형태인데 여기서 Y 나 YY 는 층 수를 나타내고 XX 는 엘리베이터에서부터 세었을 때의 번호를 나타낸다. 즉, 그림 1 에서 빗금으로 표시한 방은 305 호가 된다.

손님은 엘리베이터를 타고 이동하는 거리는 신경 쓰지 않는다. 다만 걷는 거리가 같을 때에는 아래층의 방을 더 선호한다. 예를 들면 102 호 방보다는 301 호 방을 더 선호하는데, 102 호는 거리 2 만큼 걸어야 하지만 301 호는 거리 1 만큼만 걸으면 되기 때문이다. 같은 이유로 102 호보다 2101 호를 더 선호한다.

여러분이 작성할 프로그램은 초기에 모든 방이 비어있다고 가정하에 이 정책에 따라 N 번째로 도착한 손님에게 배정될 방 번호를 계산하는 프로그램이다. 첫 번째 손님은 101 호, 두 번째 손님은 201 호 등과 같이 배정한다. 그림 1 의 경우를 예로 들면, H = 6이므로 10 번째 손님은 402 호에 배정해야 한다.

첫 번째 풀이

문제는 복잡해보이나, 결국 H를 N으로 나눈 몫 + 1이 호수가 되고, 나머지가 층 수가 된다. 각각을 구한뒤 문자열로 만들어 더 해준다.

소스코드

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
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const c = parseInt(input[0]);

for (let i = 1; i <= c; i++) {
  const line = input[i].split(" ");
  const h = parseInt(line[0]);
  const w = parseInt(line[1]);
  const n = parseInt(line[2]);
  const room = Math.ceil(n / h);
  let floor;
  let answer;

  if (n % h === 0) {
    floor = h;
  } else {
    floor = n % h;
  }

  if (room >= 10) {
    console.log(`${floor}${room}`);
  } else {
    console.log(`${floor}0${room}`);
  }
}

[백준 문제풀이] 1193번 분수찾기


1193번 분수찾기 (Bronze 1)

문제

https://www.acmicpc.net/problem/1193

무한히 큰 배열에 다음과 같이 분수들이 적혀있다.

1/1 1/2 1/3 1/4 1/5 …
2/1 2/2 2/3 2/4 … …
3/1 3/2 3/3 … … …
4/1 4/2 … … … …
5/1 … … … … …
… … … … … …
이와 같이 나열된 분수들을 1/1 → 1/2 → 2/1 → 3/1 → 2/2 → … 과 같은 지그재그 순서로 차례대로 1번, 2번, 3번, 4번, 5번, … 분수라고 하자.

X가 주어졌을 때, X번째 분수를 구하는 프로그램을 작성하시오.

첫 번째 풀이

수학 능력이 머리에 없는건지 도저히 모르겠어서 구글링을 해보았다.

수학적인 사고는 자연어로 이루어진 문제를 분석, 해결 가능한 수준으로 나누고 패턴을 찾아내는 과정이라는 말을 읽고 참 인상 깊게 새겼다. 결국 수학은 복잡한 문제를 단순화하고 패턴을 찾아내서 일반화하는 것이라는 걸 읽고 수학에 대한 새로운 생각을 할 수 있었다.

결국 주기성을 찾아내는 것이라고.

패턴화 하려고 노력했고, 답을 참고하지 않고 풀어낼 수 있었다.

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 fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim();

const num = parseInt(input);
let range = 1;
let line = 1;
let answer = 0;
let fraction = 0;

if (num === 1) {
  answer = `1/1`;
} else {
  while (range < num) {
    line++;
    range += line;
  }

  if (line % 2 === 1) {
    fraction = range - num + 1;
  } else {
    fraction = line - (range - num);
  }

  answer = `${fraction}/${line - fraction + 1}`;
}

console.log(answer);

[백준 문제풀이] 1712번 손익분기점


1712번 손익분기점 (Bronze 4)

문제

https://www.acmicpc.net/problem/1712

월드전자는 노트북을 제조하고 판매하는 회사이다. 노트북 판매 대수에 상관없이 매년 임대료, 재산세, 보험료, 급여 등 A만원의 고정 비용이 들며, 한 대의 노트북을 생산하는 데에는 재료비와 인건비 등 총 B만원의 가변 비용이 든다고 한다.

예를 들어 A=1,000, B=70이라고 하자. 이 경우 노트북을 한 대 생산하는 데는 총 1,070만원이 들며, 열 대 생산하는 데는 총 1,700만원이 든다.

노트북 가격이 C만원으로 책정되었다고 한다. 일반적으로 생산 대수를 늘려 가다 보면 어느 순간 총 수입(판매비용)이 총 비용(=고정비용+가변비용)보다 많아지게 된다. 최초로 총 수입이 총 비용보다 많아져 이익이 발생하는 지점을 손익분기점(BREAK-EVEN POINT)이라고 한다.

A, B, C가 주어졌을 때, 손익분기점을 구하는 프로그램을 작성하시오.

첫 번째 풀이

A : 고정 비용, B : 생산당 가변 비용, C : 매출

손익분기점을 구하라.

1
2
3
4
5
6
7
8
9
10
11
const answer = 0;
let 매출 = -A;

if (B > C) {
	console.log(-1);
} else {
	while (매출 > 0) {
		매출 += (C  B);
		answer++
	};
};

이와 같이 접근했다. => 시간초과

두 번째 풀이

이 문제가 ‘수학 능력’ 카테고리에 있었다는 것을 간과했었다.

반복문을 써서 풀면 당연히 늦어질 수 밖에 없고, 개당 수익으로 고정 손해 나눠준 뒤 반올림 해준 뒤 +1 해주면 되는 일이었다.

풀이 소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");

const a = parseInt(input[0]);
const b = parseInt(input[1]);
const c = parseInt(input[2]);

if (b >= c) {
  answer = -1;
} else {
  answer = Math.floor(a / (c - b)) + 1;
}

console.log(answer);

[백준 문제풀이] 2292번 벌집


2292번 벌집 (Bronze 2)

느낀 점

Keep It Super Simple

문제

https://www.acmicpc.net/problem/2292

위의 그림과 같이 육각형으로 이루어진 벌집이 있다. 그림에서 보는 바와 같이 중앙의 방 1부터 시작해서 이웃하는 방에 돌아가면서 1씩 증가하는 번호를 주소로 매길 수 있다. 숫자 N이 주어졌을 때, 벌집의 중앙 1에서 N번 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나가는지(시작과 끝을 포함하여)를 계산하는 프로그램을 작성하시오. 예를 들면, 13까지는 3개, 58까지는 5개를 지난다.

풀이

수열로 풀이 하려고 했는데, 너무 수학적으로 접근했었다는 것을 깨달았다. 그냥 간단하게 한 줄이 늘어날때마다 한 칸이 늘어나게 된다.

1까지는 1번, 7까지는 2번, 19까지는 3번, 37까지는 4번 이와 같이 이동하게 된다. 이걸 수열로 1, 7, 19와 같은 수를 수열로 생각해서 문제에 접근하였다. 입력된 숫자가 포함되는 범위까지의 수를 찾을 때까지 거리를 하나 씩 추가해주었다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require("fs");

const number = parseInt(fs.readFileSync("/dev/stdin").toString());

let distance = 1;
let range = 1;

while (number > range) {
  range += distance * 6;
  distance++;
}

console.log(distance);

2021.11.08. 백준 문제풀이


1316번 그룹 단어 체커 (Silver 5)

문제

https://www.acmicpc.net/problem/1316

그룹 단어란 단어에 존재하는 모든 문자에 대해서, 각 문자가 연속해서 나타나는 경우만을 말한다. 예를 들면, ccazzzzbb는 c, a, z, b가 모두 연속해서 나타나고, kin도 k, i, n이 연속해서 나타나기 때문에 그룹 단어이지만, aabbbccb는 b가 떨어져서 나타나기 때문에 그룹 단어가 아니다.

단어 N개를 입력으로 받아 그룹 단어의 개수를 출력하는 프로그램을 작성하시오.

풀이

먼저 set을 이용해서, 중복되는 문자가 없을 때의 경우를 제외해주고 반복문을 통해 확인하기로 정했다.

소스코드

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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().split("\n");

const n = parseInt(input[0]);
let answer = 0;

for (let i = 1; i <= n; i++) {
  const letters = input[i].split("");
  const newLetter = [];
  const set = new Set(letters);
  if (letters.length === set.size) {
    answer += 1;
  } else {
    let isGroupWord = true;
    for (let j = 0; j < letters.length; j++) {
      if (newLetter.indexOf(letters[j]) === -1) {
        newLetter.push(letters[j]);
      } else {
        if (newLetter.indexOf(letters[j]) !== newLetter.length - 1) {
          isGroupWord = false;
          break;
        }
      }
    }
    if (isGroupWord) {
      answer += 1;
    }
  }
}

console.log(answer);

반복문에서는 isGroudWord라는 Boolean 값을 통해서, GroupWord의 상태가 유지되는지 확인하였고, 다른 배열에 문자들을 추가하여서 그 배열의 중복 값이 마지막에 위치해있는지를 통해서 연속적으로 문자가 배치되어있는지 확인했다.


2021.11.07. 백준 문제풀이


2941번 크로아티아 알파벳 (Silver 5)

느낀 점

처음에는 알파벳 문자열 순서에 맞춘 조건문을 사용하려다가 너무 섹시하지 않아서 다른 방법을 고안했다. 정규표현식과 replace로 문자열 대치를 하면 쉽게 풀 수 있을 것 같아서 찾아봤는데 비슷하게 푼 사례가 있어서 참고해서 간단하게 풀 수 있었다.

에러

trim()을 사용안해서 오류가 생겼었다.

문제

https://www.acmicpc.net/problem/2941

소스코드

1
2
3
4
5
6
7
8
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim();

const regex = /c\=|c\-|dz\=|d\-|lj|nj|s\=|z\=/g;
const output = input.replace(regex, "Q");

console.log(output.length);

2021.11.06. 백준 문제풀이


5622번 다이얼 (Bronze 2)

문제

https://www.acmicpc.net/problem/5622

전화를 걸고 싶은 번호가 있다면, 숫자를 하나를 누른 다음에 금속 핀이 있는 곳 까지 시계방향으로 돌려야 한다. 숫자를 하나 누르면 다이얼이 처음 위치로 돌아가고, 다음 숫자를 누르려면 다이얼을 처음 위치에서 다시 돌려야 한다.

숫자 1을 걸려면 총 2초가 필요하다. 1보다 큰 수를 거는데 걸리는 시간은 이보다 더 걸리며, 한 칸 옆에 있는 숫자를 걸기 위해선 1초씩 더 걸린다.

상근이의 할머니는 전화 번호를 각 숫자에 해당하는 문자로 외운다. 즉, 어떤 단어를 걸 때, 각 알파벳에 해당하는 숫자를 걸면 된다. 예를 들어, UNUCIC는 868242와 같다.

할머니가 외운 단어가 주어졌을 때, 이 전화를 걸기 위해서 필요한 최소 시간을 구하는 프로그램을 작성하시오.

풀이

문자를 나눠서 각 문자를 비교할 수도 있었지만, 아스키 코드로 변환 후 대소비교를 하는 것이 더 도움이 될 것이라고 생각했다.

처음에는 숫자와 문자 사이에 규칙을 찾아서 반복문을 쓰고 싶었지만 PQRS와 WXYZ 같은 문자들 때문에 괜히 꼬일 것 같아서 단순히 조건문만 사용했다.

소스코드

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
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split("");
let answer = 0;

input.forEach((str) => {
  if (str.charCodeAt(0) <= 67) {
    answer += 3;
  } else if (str.charCodeAt(0) <= 70) {
    answer += 4;
  } else if (str.charCodeAt(0) <= 73) {
    answer += 5;
  } else if (str.charCodeAt(0) <= 76) {
    answer += 6;
  } else if (str.charCodeAt(0) <= 79) {
    answer += 7;
  } else if (str.charCodeAt(0) <= 83) {
    answer += 8;
  } else if (str.charCodeAt(0) <= 86) {
    answer += 9;
  } else if (str.charCodeAt(0) <= 90) {
    answer += 10;
  }
});

console.log(answer);

2021.11.05. 백준 문제풀이


1152번 단어의 개수 (Bronze 2)

문제

https://www.acmicpc.net/problem/1152

첫 번째 풀이

1
2
3
4
5
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");

console.log(input.length);

배운 것

86%에서 자꾸 틀려서, 예외 처리가 잘못되었다고 알았다.

어떤 경우인지 정확히 모르겠어서 찾아봤는데, 공백만 입력된 경우를 처리하지 못한 것이었다. 매우 쉬운 문제지만, 정답률이 낮은 이유가 있었다.

예외 처리

1
2
3
if (input[0] === "") {
  answer -= 1;
}

나는 이와 같이 예외처리를 해주었다.
해당 문제에 공백이 둘 연속으로 나오지 않는다고 했기 때문에, 전체를 반복문으로 세는 것보다 이것이 더 효율적이라고 생각했다.


2908번 상수 (Bronze 2)

느낀 점

아니 나는 당연히 constant를 말할 줄 알았다.
오늘도 어썸한 개발자 세상

풀이 도중 type 에러가 났었다.
알고보니 newStr 문자열을 상수로 선언해두는 바람에, 새로운 문자열이 들어갈 수 없는 에러였다. 이런걸 보면 나는 아직 브론즈가 맞는데, 오늘 실버로 승급해버렸다. 이건 마치 갓면허 땄을 때의 기분이다.

문제

https://www.acmicpc.net/problem/2908

상근이의 동생 상수는 수학을 정말 못한다. 상수는 숫자를 읽는데 문제가 있다. 이렇게 수학을 못하는 상수를 위해서 상근이는 수의 크기를 비교하는 문제를 내주었다. 상근이는 세 자리 수 두 개를 칠판에 써주었다. 그 다음에 크기가 큰 수를 말해보라고 했다.

상수는 수를 다른 사람과 다르게 거꾸로 읽는다. 예를 들어, 734와 893을 칠판에 적었다면, 상수는 이 수를 437과 398로 읽는다. 따라서, 상수는 두 수중 큰 수인 437을 큰 수라고 말할 것이다.

두 수가 주어졌을 때, 상수의 대답을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 칠판에 적은 두 수 A와 B가 주어진다. 두 수는 같지 않은 세 자리 수이며, 0이 포함되어 있지 않다.

첫 번째 풀이

라인을 입력받고, 두 수를 공백으로 split한 다음 각각 문자열에 reverse() 메서드를 사용한 뒤 숫자로 변환해서 대소 비교를 하자.

! 아이디어

3자리 수인데 배열로 변환, reverse() 사용 후, 다시 join()을 사용하는 것보다 그냥 반복문을 쓰는게 낫지 않을까?

최종 풀이

결국 반복문을 사용해서 풀었다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");
const a = input[0];
const b = input[1];

function reverseStr(str) {
  let newStr = "";
  for (let i = str.length - 1; i >= 0; i--) {
    newStr = newStr + str[i];
  }
  return newStr;
}

const newA = parseInt(reverseStr(a));
const newB = parseInt(reverseStr(b));

if (newA > newB) {
  console.log(newA);
} else {
  console.log(newB);
}

2021.11.04. 백준 문제풀이


4673번 셀프 넘버 (Silver 5)

문제

https://www.acmicpc.net/problem/4673

느낀 점

수학적인 문제는 처음이었지만, 재미있게 풀 수 있었다.

기억에 남는 것은 디버깅 결과 dArray에 특정 수 이상이 입력되지 않았던 것인데 조건문의 사용이 잘못되어서였다.


11654번 아스키 코드 (Bronze 5)

문제

https://www.acmicpc.net/problem/11654

배운 것

문자열을 아스키 코드로 변환해주는 메서드 : charCodeAt(문자열 자릿수)

아스키 코드를 문자열로 변환해주는 메서드 : fromCharCode(아스키코드 번호)


10809번 알파벳 찾기 (Bronze 2)

문제

https://www.acmicpc.net/problem/10809

배운 것

join() 메서드는 배열의 모든 값을 연결해 하나의 문자열로 만들어준다.

이때, parameter로 받은 값을 모든 값 사이에 separator로 넣어준다.

처음엔 문자열에 공백과 값을 추가하는 방식으로 출력했는데, 메모리 초과가 나와서 이 방법을 사용했다.


2675번 문자열 반복하기 (Bronze 2)

문제

https://www.acmicpc.net/problem/2675

배운 것

자바스크립트에서는 repeat(반복 횟수) 메서드를 통해서 문자열을 반복할 수 있다.


2021.11.02. 백준 문제풀이


3052번 나머지 (Bronze 2)

문제

https://www.acmicpc.net/problem/3052

느낀 점

배열에서는 for보다 forEach를 쓰자.

trim 사용을 생활화 하자.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().split("\n");

const bal = [];

input.forEach(x => {
    const num = x % 42;
    if (bal.indexOf(num) === -1) {
        bal.push(num);
    };
});

console.log(bal.length);
});

2021.11.01. 백준 문제풀이


10951번 A + B – 4 (Bronze 3)

문제

https://www.acmicpc.net/problem/10951

느낀 점

반복문을 사용할 때 카운팅 변수의 흐름을 잘 생각하자.


1110번 더하기 사이클 (Bronze 1)

문제

https://www.acmicpc.net/problem/1110

느낀 점

  1. break를 사용하는 while문에 익숙해지자.
  2. 나머지와 몫을 사용해 자릿 수 별 숫자를 구하는 방식을 이해하자.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const fs = require("fs");

const input = parseInt(fs.readFileSync("/dev/stdin").toString());

let count = 0;
let num = input;
let onenumber, tennumber;

while (true) {
    count++;
    tennumber = Math.floor(num / 10);
    onenumber = num % 10;
    num = (onenumber * 10) + ((onenumber + tennumber) % 10);
    if (num === input) break;
}

console.log(count)
});

2021.10.31. 백준 문제풀이


14681번 사분면 고르기 (Bronze 4)

문제

https://www.acmicpc.net/problem/14681

배운 것

소스코드

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 readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let input = [];

rl.on("line", (line) => {
  input.push(parseInt(line));
}).on("close", () => {
  const x = input[0];
  const y = input[y];

  let output = 1;

  if (x < 0) {
    output = 2;
    if (y < 0) {
      output = 3;
    }
  } else if (y < 0) {
    output = 4;
  }

  console.log(output);

  process.exit();
});

2884번 알람 시계

문제

https://www.acmicpc.net/problem/2884

느낀 점

toString()을 toSting()으로 적어놨었다.

카페 올 때 Englishman in newyork을 들으면서 왔었는데 그 탓이지 싶다.

앞으로 에러가 발생하면 오타를 먼저 찾는 습관을 길러야겠다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const fs = require("fs");

const input = fs.readFileSync("/dev/stdin").toString().split(" ");

let hour = parseInt(input[0]);
let minute = parseInt(input[1]);

if (minute - 45 < 0) {
  if (hour == 0) {
    hour = 23;
  } else {
    hour -= 1;
  }
  minute += 15;
} else {
  minute -= 45;
}

console.log(`${hour} ${minute}`);

2021.10.29. 백준 문제풀이


10171번 고양이 (Bronze 5)

문제

https://www.acmicpc.net/problem/10171

배운 것

문자열 이스케이프 문자, 역슬래시(\) : 어떤 한 문자를 코드가 아닌 문자로 만들어주는 문자

역슬래시를 문자 그대로 쓰려면 \\ 와 같은 형태로 작성해야함. 이외에도
' : 작은따옴표
" : 큰따옴표
\n : 개행
\t : 탭


1000번 A + B (Bronze 5)

문제

https://www.acmicpc.net/problem/1000

배운 것

브라우저에서 작동되는 js 언어는 값을 입력받을 수 없어서, Node.js의 fs 모듈을 이용해서 진행해야한다.

fs 모듈 : FileSystem 모듈로, 파일 처리와 관련된 작업을 한다.

fs.readFile(filename, [options], callback) : filename의 파일을 [options]의 방식으로 읽은 후 callback으로 전달된 함수를 호출함. (비동기적)

fs.readFileSync(filename, [options]) : filename의 파일을 [options]의 방식으로 읽은 후 문자열을 반환함.(동기적)

fs 모듈로 stdin을 로드 fs 모듈을 이용하여 표준입력(stdin) 파일을 동기적으로 불러오는 방법
=> 한 칸 띄어쓰기로 구분되게 된다.

fs 모듈을 통해 문자열로 불러와졌기 때문에, parseInt를 통해 숫자로 변환해주어야 한다.


10869번 사칙 연산 (Bronze 5)

문제

https://www.acmicpc.net/problem/10869

느낀 점

문제에서 예제 출력을 잘 읽고, 출력형태가 어떤지 확인해서 Type이나 소수점을 신경써주자.


Check out other tags: