치춘짱베리굿나이스

[백준] 14888 본문

연산자 끼워넣기

문제

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱셈(×)의 개수, 나눗셈(÷)의 개수이다.

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.

풀이

let n;
let input;
let algebraNum; // + - * /
let algStack = [];
let min = 1000000000;
let max = -1000000000;
let ans = 0;

const doAlgebra = () => {
  let i = 1;
  let sum = input[0];
  for (let v of algStack) {
    if (v === "+") sum += input[i];
    else if (v === "-") sum -= input[i];
    else if (v === "*") sum *= input[i];
    else if (v === "/") {
      if (sum < 0) sum = Math.floor(-sum / input[i]) * -1;
      else sum = Math.floor(sum / input[i]);
    }
    i++;
  }
  ans++;
  if (sum < min) min = sum;
  if (sum > max) max = sum;
};

const getAlgebra = (algIdx) => {
  if (algIdx === 0) return "+";
  else if (algIdx === 1) return "-";
  else if (algIdx === 2) return "*";
  else if (algIdx === 3) return "/";
};

const recurAlgeb = (k, algIdx) => {
  if (k === n - 1) doAlgebra();
  else if (algebraNum[algIdx] === 0) return;
  else {
    algStack.push(getAlgebra(algIdx));
    algebraNum[algIdx]--;
    recurAlgeb(k + 1, 0);
    recurAlgeb(k + 1, 1);
    recurAlgeb(k + 1, 2);
    recurAlgeb(k + 1, 3);
    algebraNum[algIdx]++;
    algStack.pop();
  }
};

const algeb = () => {
  input = require("fs")
    .readFileSync("/dev/stdin")
    .toString()
    .trim()
    .split("\n")
    .map((v) => v.split(" ").map(Number));
  n = input[0][0];
  algebraNum = input[2];
  input = input[1];
  recurAlgeb(0, 0);
  recurAlgeb(0, 1);
  recurAlgeb(0, 2);
  recurAlgeb(0, 3);
  console.log(`${max}\n${min}`);
};

algeb();

반성회

  1. 연산자를 스택에 (algStack) 넣고, 연산자를 n - 1개 채웠을 때 실제 연산을 진행한 뒤 최댓값과 최소값을 판별
  2. 재귀함수의 첫 번째 인자 (k) 는 지금 추가하는 연산자의 인덱스, 두 번째 인자 (idx) 는 연산자의 종류 (0 = +, 1 = -, 2 = *, 3 = /)
  3. 재귀함수의 탈출조건은 연산자를 n - 1개 다 채웠거나 (doAlgebra() 함수를 통해 실제 연산 진행 및 최대최소값 판별) 지금 추가하고자 하는 연산자를 다 사용했을 때 (남은 갯수가 0개일 때)
  4. 재귀함수 (recurAlgeb) 내에선
    1. 먼저 지금 인자로 들어온 연산자의 종류를 스택에 push (algStack.push(getAlgebra(algIdx));)
    2. 지금 인자로 들어온 연산자의 개수 1 감소 (algebraNum[algIdx]--;)
    3. 다음 인덱스 (k + 1) 에 연산자 종류 0, 1, 2, 3을 각각 넣어서 재귀 진행
    4. 지금 인자로 들어온 연산자의 개수 1 증가 (없던 일로 침)
    5. 마지막으로 들어온 (현재 재귀 레벨에서 추가한) 연산자를 스택에서 pop (추가하지 않은 것으로 침)
  5. doAlgebra() 내에선 스택에 들어온 각각의 사칙연산 기호와 input으로 받은 숫자를 이용해 계산하고, 현재 최소값보다 작으면 최소값 갱신, 최대값보다 크면 최대값 갱신
  6. 마지막에 한번에 출력

'Javascript + Typescript > 자바스크립트로 알고리즘풀기' 카테고리의 다른 글

[백준] 5522  (0) 2022.05.08
[백준] 3046  (0) 2022.05.08
[백준] 1312  (0) 2022.05.08
[백준] 1759  (0) 2022.05.08
[백준] 5337  (0) 2022.05.08
Comments