백준 1712번 풀이

문제

손익분기점을 구하는 문제. 참고로 부등식이다.

방정식과 부등식

방정식은 식에 등호(=)가 있고, 부등식은 식에 부등호(<,>)가 있다. (대충 펀쿨섹좌 짤)

그래서 x+y=1은 방정식이고, x+y>1은 부등식이다. 여기서 왜 부등식을 쓰느냐… 손익분기점은 비용보다 커야 하기 때문. 어디서 많이 본 것 같다고? 저거 중학생때 배우는겁니다 여러분.

일반화

그럼 이제 일반화를 해 보도록 하자.

고정비용 A, 가변비용 B가 있고 가변비용은 생산량에 따라 비용이 배가된다. 즉 물건을 하나 생산할 때는 A+B, 두 개 생산할 때는 A+2B 이런 식이 된다. 그리고 C는 물건의 판매가. 그래서 일반화한 식이 A + Bx < Cx가 된다. 그리고 이항정리를 하게 되면 A < Cx – Bx를 거쳐서 A < (C – B)x가 된다.

그리고 이 식을 다시 x에 맞게 정리하게 되면 최종적으로 A / (C – B) < x가 된다.

풀이

일단 Python에서도 모듈을 깔면 방정식을 풀 수 있다. 바로 sympy와 mpmath.

import sys
from sympy import *
from mpmath import mp
init_printing()
# Sympy
A,B,C=map(int,sys.stdin.readline().split(" "))
x=Symbol('x')
# A, B, C는 입력받는 변수(고정비용, 가변비용, 판매가)
# 이 때 손익분기점에 대한 부등식은 A+Bx<Cx이다. 
expr=A < (C - B) * x
print(solve(expr))

x를 심볼화하고 solve() 던지면

(10 < x) & (x < oo)

이렇게 나온다. 그러면 x가 10보다 커야 하니 제일 가까운 정수는 11. 근데 이걸로 내면 출력 형식때문에 틀려요…

import sys
A,B,C=map(int,sys.stdin.readline().split(" "))
# A, B, C는 입력받는 변수(고정비용, 가변비용, 판매가)
# 이 때 손익분기점에 대한 부등식은 A+Bx<Cx이다. 항을 정리해주게 되면 A < (C - B)x가 된다. 
# 단, sympy를 부를 수 없으므로... 
x = 0
y = C - B
while x * y <= A:
    x += 1
    x * y
print(x)
# 뺑뺑이 돌릴거다.

그럼 어쩌겠음 뺑뺑이 돌려야지. 근데 문제가 하나 있다. 이 코드도 맞는 답을 내놓는 건 맞는데, 이걸로 주면 시간초과 나온다. 그리고 저 코드는 한가지가 빠졌다. 바로 답이 없는 경우에 -1로 처리하는 코드가 없다. 그니까 아싸 끝 하고 저거 갖다 내면 뭐다? 틀린다.

import sys
A,B,C=map(int,sys.stdin.readline().split(" "))
# A, B, C는 입력받는 변수(고정비용, 가변비용, 판매가)
# 이 때 손익분기점에 대한 부등식은 A+Bx<Cx이다. 항을 정리해주게 되면 A < (C - B)x가 된다. 
# 단, sympy를 부를 수 없으므로... 
x = 0
y = C - B
if y < 0:
    print(-1)
else: 
    while x * y <= A:
        x += 1
        x * y
    print(x)
# 뺑뺑이 돌릴거다.

손익분기점이 있는 케이스와 없는 케이스 둘 다 sympy로 만든 코드에 넣었을 때 해가 어떻게 되느냐, 손익분기점이 있는 케이스는 C – B, 즉 판매 비용과 가변 비용의 차가 양수이고 손익분기점이 없는 케이스(3 2 1)는 C – B가 음수, 즉 0보다 작다. 거기에 대한 처리를 한 게 저 코드인데 아직 내지 말아봐… 저거 시간초과여.

import sys
A,B,C=map(int,sys.stdin.readline().split(" "))
# A, B, C는 입력받는 변수(고정비용, 가변비용, 판매가)
# 이 때 손익분기점에 대한 부등식은 A+Bx<Cx이다. 항을 정리해주게 되면 A < (C - B)x가 된다. 
# 단, sympy를 부를 수 없으므로... 
x = 0
if C - B < 0:
    print(-1)
else:  
    x = A / (C - B)
    print(int(x+1))
# 루프문 시간초과 실화냐고

루프를 안 돌리고 다이렉트로 구해버리면 시간초과는 해결이다. 와! 그럼 내도 돼요? 아니 있어봐 저거 내면 Zerodivision 떠… 선생님 되게 행복회로 태웠는데 에러나면 엿같잖아요…

import sys
A,B,C=map(int,sys.stdin.readline().split(" "))
# A, B, C는 입력받는 변수(고정비용, 가변비용, 판매가)
# 이 때 손익분기점에 대한 부등식은 A+Bx<Cx이다. 항을 정리해주게 되면 A < (C - B)x가 된다. 
# 단, sympy를 부를 수 없으므로... 
x = 0
if C - B <= 0:
    print(-1)
else:  
    x = A / (C - B)
    print(int(x+1))
# 루프문 시간초과 실화냐고

Zerodivision은 어떤 수를 0으로 나누려고 하면 생기는 문제이다. n / 0 (n != 0)이면 불능, 0 / 0은 부정이다. 아무튼 그러한 이유로 C – B가 0일때도 -1이 뜨도록 처리해야 한다.

Appendix: 부정과 불능

부정은 해가 더럽게 많은거고, 불능은 알파고 할아버지가 와도 답이 없는 케이스. 알파고는 할아버지가 없는데요 

0으로 나누는 케이스의 경우 0 / 0이 부정인 이유를 이해하려면 나눗셈이 곱셈과 서로 역연산 관계라는 것을 알고 가야 한다. 부정은 해가 개 많은거라고 했는데, 0 / 0 = C라고 하면 0 = C * 0이 되고 여기에 들어갈 수 있는 C가 사실상 정말 겁나 완전 핵 많기 때문에 부정. 불능은 n / 0 = C (단, n != 0)라고 할 때 n = C * 0이고 이를 만족하는 C가 없으므로 불능.

계산기에서 0으로 나누면 에러 뜨는 이유도 그것때문이다. 나눗셈이라는 건 피제수에서 제수를 빼고 빼고 빼고 하는거고, 최종적으로 몇 번 뺐느냐가 몫, 얼마 남았느냐가 나머지(몫보다 작은 수)가 되는데 0을 빼게 되면 아주 주구장창 0만 빼야 한다. 사용자가 종료하거나(…) 비스무트 방사성 동위원소 반감기 도래하거나(대략 (1.9±0.2) ×1019년) 우주 멸망하거나 프로그램이 뻗을때까지 해야 한다. 대충 break 조건 없는 while True: 에 갇힌다고 생각하면 된다.그래서 대부분의 프로그램이나 계산기는 0으로 나누려고 하면 에러를 토한다.

아, !=는 다르다는 얘기다.