백준

19532_수학은 비대면 강의입니다 NodeJS 연립방정식 풀이

mynamemj 2025. 5. 30. 13:41
반응형

알고리즘 분류는 아래와 같다.

  • 브루트포스
  • 수학

이런거 안보고 그냥 풀어서 항상 고생하는데 이번엔 알고리즘 분류를 봤어도 고집좀 부렸을 것 같다. 브루트포스를 처음 봤다. 그동안 풀어본적 있을것 같긴 한데 신경을 안쓰니 알 턱이 있나. 쉽게 말하자면 무식하게 확인하는 알고리즘이다. 완전탐색 알고리즘으로 범위가 정해지면 그 범위를 모두 확인해야하는 문제였다.
범위가 -999부터 999라 그리 많지는 않지만 이렇게 반복문을 돌리는걸 선호하지 않는다. 문제를 풀고나면 지피티한테도 더 똑똑한 방법을 꼭 물어보는 편이다. 그래서 난 반복문을 돌지 않고 연립방정식을 풀어봤다. 자꾸 틀려서 구글링을 좀 해봤는데 연립방정식을 정리해서 푸신분은 없더라... 안되서 안하시는건지 알고리즘 분류에 충실하신건지 몰라도 내 방법을 소개해보겠다.

접근방식

간단하다. 학생때 배운 일차연립방정식 그대로 풀어준다.

  1. x와 y를 구할 수 있는 상수로 이루어진 식으로 정리한다.
  2. 주어진 값을 대입한다.
  3. 끝.

연립일차방정식 정리하는건 큰 문제가 아니니 간단하게 끝내고 넘어갈거라 생각했지만 오래걸렸다. 문자로만 이루어져있어 헷갈리기도 했고, 부동소수점이 문제였다.

문제

문제에서 x와 y가 유일한 해고 정수임을 보장한다고 해서 다른걸 신경 안썼지만 그건 for문 돌릴때 효과가 있지 내 방식에서는 내가 x와 y의 조건을 보증해서 출력해야했다.

1-1 부동소수점

console.log(0.1 + 0.2) // 0.30000004 였나?

자바스크립트로 연산을 하다보면 자주 발견된다. 나눗셈에서 발생한다고 생각하던건데 덧셈이든 뭐든 항상 발생한다. 이 문제때문에 1.9999와 같이 잘못 계산된 숫자를 어떻게 처리할 것인가를 생각해야 한다.

나는 생각없이 올림처리를 했었다. 그 이유로 테스트를 해보다가 1.99999와 같은 값을 발견했어서인데 원래 2일거라고 생각하고 올림처리를 했었다.

// x를 이용해서 y 구할 때
const x = (f - (e * c) / b) * (b / ((b*d) - (a*e)))
const y = (c - a * x) / b;
// 전체 다 구한 값에 올림처리
const x = Math.ceil((c * e - b * f) / (a * e - b * d));
const y = Math.ceil((a * f - c * d) / (a * e - b * d));

첫 번째 시도에서 x와 y를 계산할 때 x 값이 부동소수점으로 표시되어서 y도 틀렸나? 싶어서 두 번째에 있는 것처럼 y값에서 x에 의존을 없애고 올림처리를 했다. 하지만 역시나 틀림;;;

예제는 고작 2개고 내가 예제를 만들기는 싫어서 수정하고 정답 돌리기만 하는데 꼭 70퍼센트대였나? 거기서 틀렸다고 처리된다. 이건 내가 어떤 케이스에 대해서 실패한거니까 더 생각을 해봐야 했다.

1-2 Math.floor와 paseInt

생각을 하다하다 AI한테 생각을 넘겼다. 내 머리로는 이틀째에 포기.. 그래서 받은 답은 floor와 parseInt인데, 왜 내림인지 사실 아직도 깨닫지 못했다. 0.1 + 0.2의 경우 내림이 맞다. 부족하게 계산된 경우도 있지 않나? 원래 2인데 1.9999로 나왔던 기억이 있어서 차라리 반올림이지 왜 내림인지는 이해하지 못했다.

결국 paseInt를 이용해서 정답은 처리되었다. 정답하고 땡은 아니라 floor도 써봤는데 이건 틀렸다. 문득 궁금해진 두 녀석의 차이.

// 땡
const x = Math.floor((c * e - b * f) / (a * e - b * d));
const y = Math.floor((a * f - c * d) / (a * e - b * d));
// 정답
const x = parseInt((c * e - b * f) / (a * e - b * d));
const y = parseInt((a * f - c * d) / (a * e - b * d));

둘 다 정수 부분을 얻기위해 사용한다. 특히 parseInt는 진법 변환에서도 사용했었다. 8진수 숫자를 10진수로 바꿔준다거나 하는 문제에서 사용했다. floor나 parseInt 모두 정수부분을 잘라주지만 음수에서 차이가 있었다. floor의 경우 음수에서 작은 숫자로 내림을 한다. -3.14를 내린다면 -3이 아닌 -4가 나온다. 반면 parseInt는 정수부분 그 자체인 -3을 준다. 때문에 console.log(Math.floor(1.9999) === parseInt(1.9999));true 지만 console.log(Math.floor(-1.9999) === parseInt(-1.9999));false다.

정답

prettier-ignore는 무시해도 된다. 중요한건 parseInt를 쓴다는 것. 정석대로라면 for문으로 공식에 적합한 x와 y를 찾도록 하자.

const fs = require("fs");
const filePath = process.platform === "linux" ? "dev/stdin" : "input.txt";
const [a, b, c, d, e, f] = fs
  .readFileSync(filePath)
  .toString()
  .trim()
  .split(" ")
  .map(Number);

// prettier-ignore
const x = parseInt((c * e - b * f) / (a * e - b * d));
const y = parseInt((a * f - c * d) / (a * e - b * d));
console.log(x, y);
반응형