[알고리즘] N으로 표현

2022. 6. 15. 18:49·알고리즘
반응형

문제 설명

아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.

12 = 5 + 5 + (5 / 5) + (5 / 5)
12 = 55 / 5 + 5 / 5
12 = (55 + 5) / 5

5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.
이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.

제한사항

  • N은 1 이상 9 이하입니다.
  • number는 1 이상 32,000 이하입니다.
  • 수식에는 괄호와 사칙연산만 가능하며 나누기 연산에서 나머지는 무시합니다.
  • 최솟값이 8보다 크면 -1을 return 합니다.

입출력 예

N number return
5 12 4
2 11 3

입출력 예 설명

예제 #1
문제에 나온 예와 같습니다.

예제 #2
11 = 22 / 2와 같이 2를 3번만 사용하여 표현할 수 있습니다.

나의 풀이

const N = 5;
const number = 12;

function formula(N, expression, array) {
  const type = ["*", "+", "-", "/"];
  const before = expression[expression.length - 1];
  const open = expression.split("(").length;
  const close = expression.split(")").length;

  if (expression.split(N).length > 8) {
    return;
  } else if (!type.includes(before)) {
    const exp = expression.split(N);
    array.push(expression.replace(exp.pop(), ""));
  }

  if (before === N)
    for (let i = 0; i < type.length; i++) {
      formula(N, expression + type[i], array);
    }

  if (before !== ")") formula(N, expression + N, array);

  if (open === close) {
    if (before !== N && before !== ")") {
      if (expression === "5*5*5*5*(5*5") {
        console.log("????");
      }
      formula(N, expression + "(", array);
    }
  } else if (before !== "(" && !type.includes(before)) {
    formula(N, expression + ")", array);
  }
}

function solution(N, number) {
  var answer = 0;
  let value = { "+": 1, "-": 2, "*": 3, "/": 4, "(": 5, ")": 0 };
  let arr = [];

  formula(N.toString(), "5", arr);

  arr = [...new Set(arr)];

  console.log(arr.length, arr);

  for (let i = 4999; i < 5000; i++) {
    const reg = /[*\-()\/+]/g;
    const reg2 = /[^*\-()\/+]/g;
    let exp = "5+5*5+(5+5-55/5)";
    const open = exp.split("(").length;
    const close = exp.split(")").length;

    if (open !== close) {
      exp += ")";
      exp = exp.replace("()", "");
    }
    const num = exp.split(reg).filter((data) => data);
    const ex = exp.split(reg2).join("").split("");

    console.log(num, ex, exp);

    const stack = [];
    for (let j = 0; j < ex.length; j++) {
      const num1 = num[j];
      const num2 = num[j + 1];

      const next = ex[j + 1];

      if (value[ex[j]] >= ex[j + 1]) {
        let sum = 0;
        switch (ex[j]) {
          case "+":
            sum = num1 + num2;
            break;
          case "-":
            sum = num1 - num2;
            break;
          case "*":
            sum = num1 * num2;
            break;
          case "/":
            sum = parseInt(num1 / num2);
            break;
        }
      }
    }
  }

  return answer;
}

solution(N, number);

최초엔 재귀함수를 사용해서 나올 수 있는 모든 경우의 수를 식으로 표현해서 계산을 하려고 작업했다. 

식으로 나타내는것은 가능했는데, 일반적인 계산을 하고자하니, 중위식 계산으론 작업이 어렵다는 것을 느꼇다. 

 

const N = 5;
const number = 12;

// 연속된 괄호
function changeIntoPost(expression) {
  const stack = [];
  let postfixString = [];
  let temp = "";

  for (let i = 0; i < expression.length; i++) {
    let ch = expression[i];

    if (ch === "(") {
      stack.push(ch);
    } else if (ch === ")") {
      let oper = stack.pop();
      postfixString.push(temp);
      while (stack.length !== 0 && oper !== "(") {
        postfixString.push(oper);
        oper = stack.pop();
      }
      temp = "";
    } else if (ch === "+" || ch === "-") {
      if (stack.length === 0) {
        postfixString.push(temp);
        stack.push(ch);
        temp = "";
      } else {
        postfixString.push(temp);
        while (stack.length !== 0) {
          let oper = stack.pop();

          if (oper === "(") {
            stack.push(oper);
            break;
          }
          postfixString.push(oper);
        }

        temp = "";
        stack.push(ch);
      }
    } else if (ch === "*" || ch === "/") {
      if (stack.length === 0) {
        postfixString.push(temp);
        stack.push(ch);
        temp = "";
      } else {
        postfixString.push(temp);
        while (stack.length !== 0) {
          let oper = stack.pop();

          if (oper === "(" || oper === "+" || oper === "-") {
            stack.push(oper);
            break;
          }
          postfixString.push(oper);
        }

        temp = "";
        stack.push(ch);
      }
    } else {
      temp += ch;
    }
  }

  postfixString.push(temp);

  while (stack.length !== 0) {
    let oper = stack.pop();

    if (oper === "(") {
      stack.push(oper);
      break;
    }
    postfixString.push(oper);
  }

  return postfixString.filter((data) => data);
}

function evalPostfix(expression, number, N) {
  let exp = changeIntoPost(expression);

  let stack = [];

  let num1 = 0;
  let num2 = 0;

  for (let i = 0; i < exp.length; i++) {
    ch = exp[i];
    console.log(stack);

    if (ch === "+" || ch === "-" || ch === "*" || ch === "/") {
      num2 = parseInt(stack.pop());
      num1 = parseInt(stack.pop());

      switch (ch) {
        case "+":
          stack.push(num1 + num2);
          break;
        case "-":
          stack.push(num1 - num2);
          break;
        case "*":
          stack.push(num1 * num2);
          break;
        case "/":
          stack.push(parseInt(num1 / num2));
          break;
      }
    } else {
      stack.push(ch);
    }
  }

  let test = stack.pop();

  if (number === test) {
    return expression.split(N).length;
  } else {
    return 999;
  }
}

function formula(N, expression, array) {
  const type = ["*", "+", "-", "/"];
  const before = expression[expression.length - 1];
  const open = expression.split("(").length;
  const close = expression.split(")").length;

  if (expression.split(N).length > 8) {
    return;
  } else if (!type.includes(before)) {
    const exp = expression.split(N);
    const exp2 = expression.split(N);

    array.push(expression.replace(exp.pop(), ""));
  }

  if (before === N || before === ")")
    for (let i = 0; i < type.length; i++) {
      formula(N, expression + type[i], array);
    }

  if (before !== ")") formula(N, expression + N, array);

  if (open === close) {
    if (before !== N && before !== ")") {
      formula(N, expression + "(", array);
    }
  } else if (before !== "(" && !type.includes(before)) {
    formula(N, expression + ")", array);
  }
}

function solution(N, number) {
  var answer = 999;

  let arr = [];

  let strN = N.toString();

  formula(strN, "", arr);

  arr = [...new Set(arr)];

  console.log(arr);

  for (let i = 0; i < arr.length; i++) {
    let exp = arr[i];

    const open = exp.split("(").length;
    const close = exp.split(")").length;

    if (open !== close) {
      exp += ")";
      exp = exp.replace("()", "");
    }

    if (exp === "5/(55+5)") {
      console.log("???", evalPostfix("5/(55+5)", number, N));
    }
    // answer = Math.min(answer, evalPostfix(exp, number, N));
  }

  return answer;
}

solution(N, number);

let i = 10;

그래서 후위식으로 변환해서 푸는 방식을 고려해봤다. 

 

이번에는 문제는 풀렸는데, 재귀식으로 사용하는 것 자체가 경우의 수가 너무 많아서 

한번 실행하면 몇 십분이 걸리는 문제가 발생했다. 

 

const N = 5;
const number = 55555555;

function solution(N, number) {
  let answer = -1;
  const set = Array.from(new Array(9), () => new Set());

  for (let i = 1; i <= 8; i++) {
    set[i].add(parseInt(N.toString().repeat(i)));

    for (let j = i; j > 0; j--) {
      for (let item of set[i - j]) {
        for (let item2 of set[j]) {
          set[i].add(parseInt(item + item2));
          set[i].add(parseInt(item - item2));
          set[i].add(parseInt(item * item2));
          set[i].add(parseInt(item / item2));
        }
      }
    }

    if (set[i].has(number)) {
      answer = i;
      break;
    }
  }

  return answer;
}

solution(N, number);

솔직히 이 방식은 힌트를 받고 문제를 풀었는데, 

N이란 숫자와 사칙연산을 활용해서 결과를 도출할 때는, 

 

N을 1개 쓸때는 5,

N을 2개 쓸때는 55와 N1 과 N1을 사칙연산,

N을 3개 쓸때는 555와 N1 과 N2를 사칙연산, N2와 N1을 사칙연산,

N을 4개 쓸때는 5555와 N1 과 N3를 사칙연산, N3 와 N1을 사칙연산, N2 와 N2를 사칙연산 

 

처럼 공식이 있었다. 

 

공식을 알아내니 쉽게 코딩을 할 수 있었다. 

Array에 Set을 넣어준 이유는 중복된 값을 하나로 만들어 사칙연산을 할 때 조금이라도 빨라지게 하기 위해서 사용했다. 

 

이번 문제를 푸는데, 오전 11시부터 6시까지 총 7시간이 걸렸다. 

물론 중간에 물, 화장실 등 사용한 시간이 있었지만 처음으로 알고리즘 문제를 푸는데 벽을 느끼게 되었다. 

 

출처: 프로그래머스 코딩 테스트 연습, https://programmers.co.kr/learn/challenges

반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘' 카테고리의 다른 글

[알고리즘] 프린터  (1) 2022.06.17
[알고리즘] 튜플  (0) 2022.06.16
[알고리즘] 수식 최대화  (0) 2022.06.09
[알고리즘] 거리두기 확인하기  (0) 2022.06.05
[알고리즘] 뉴스 클러스터링  (0) 2022.06.03
'알고리즘' 카테고리의 다른 글
  • [알고리즘] 프린터
  • [알고리즘] 튜플
  • [알고리즘] 수식 최대화
  • [알고리즘] 거리두기 확인하기
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    식물
    webpack
    Node.js
    프로그래머스
    바질
    ChatGPT
    Docker
    리얼학습일기
    바질 키우기
    타입스크립트
    다이소
    CSS
    알고리즘
    react
    자바스크립트
    네트워크
    리얼클래스
    네이버 부스트캠프
    덤프
    ReactNative
    CCNA
    redux
    리액트
    영어독학
    영어회화
    javascript
    Babel
    typescript
    next.js
    타일러영어
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[알고리즘] N으로 표현
상단으로

티스토리툴바