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