[BOJ] 1712 손익분기점 (JAVA)

joyful·2021년 4월 13일
0

Algorithm

목록 보기
51/65

✅ 문제

월드전자는 노트북을 제조하고 판매하는 회사이다. 노트북 판매 대수에 상관없이 매년 임대료, 재산세, 보험료, 급여 등 A만원의 고정 비용이 들며, 한 대의 노트북을 생산하는 데에는 재료비와 인건비 등 총 B만원의 가변 비용이 든다고 한다.

예를 들어 A=1,000, B=70이라고 하자. 이 경우 노트북을 한 대 생산하는 데는 총 1,070만원이 들며, 열 대 생산하는 데는 총 1,700만원이 든다.

노트북 가격이 C만원으로 책정되었다고 한다. 일반적으로 생산 대수를 늘려 가다 보면 어느 순간 총 수입(판매비용)이 총 비용(=고정비용+가변비용)보다 많아지게 된다. 최초로 총 수입이 총 비용보다 많아져 이익이 발생하는 지점을 손익분기점(BREAK-EVEN POINT)이라고 한다.

A, B, C가 주어졌을 때, 손익분기점을 구하는 프로그램을 작성하시오.

✅ 입력

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 21억 이하의 자연수이다.

✅ 출력

첫 번째 줄에 손익분기점 즉 최초로 이익이 발생하는 판매량을 출력한다. 손익분기점이 존재하지 않으면 -1을 출력한다.

✅ 예제 1

▼ 입력

1000 70 170

▼ 출력

11

✅ 예제 2

▼ 입력

3 2 1

▼ 출력

-1

풀이

이 문제에서 생각해봐야 할 점은 '손익분기점이 존재하지 않는 경우'를 구현하는 부분이다.
조건이 성립하지 않으면 반복문을 빠져나와 -1을 출력해야 하는데, 노트북 대수가 정확히 주어져 있지 않기 때문에 조건을 어떻게 지정해야 하나 싶을 것이다. 그렇다고 무한정 루프를 실행시킬 수도 없는 노릇 아닌가.

다행히도 노트북 대수를 int 타입으로 지정하면, int 타입의 범위값을 초과할 경우 자연스레 반복문을 빠져 나오게 된다.
하지만 여기서 또 문제가 발생하는데, 아무리 -1을 대입해도 -2147483648이 출력된다는 것이다. 다음 표를 살펴보자.

[Java 정수 타입 자료형의 표현 범위]

자료형사이즈표현 범위
byte1 byte-128 ~ 127
short2 byte-32768 ~ 32767
int4 byte-2147483648 ~ 2147483647
long8 byte-922337036854775808 ~ 9223372036854775807

위의 표를 보면, int 타입의 범위는 -2147483648 ~ 2147483647이다. java에서는 정수값이 허용된 범위를 초과하게 되면 시스템 에러가 발생하여 의도한 수와는 다른 수가 저장되는 이른바 Overflow가 발생하게 된다.

근데 여기서 이상한 점이 있다. 이 문제에서는 최대값을 초과하면 2147483648이 출력되야 하는데, 이상하게도 -2147483648이 출력된다. 왜일까?
그 이유는 값을 2진수로 출력해보면 알 수 있다.
다음 예제를 보자.

System.out.println(Integer.toBinaryString(-2147483648));
// 결과 : 10000000 00000000 00000000 00000000

System.out.println(Integer.toBinaryString(-1));
// 결과 : 11111111 11111111 11111111 11111111

System.out.println(Integer.toBinaryString(1));
// 결과 : 00000000 00000000 00000000 00000001

System.out.println(Integer.toBinaryString(2147483647));
// 결과 : 01111111 11111111 11111111 11111111


java에서 기본 정수 타입은 signed이므로, 최상위 비트를 부호 비트로 사용한다. 그래서 2147483648int의 범위에서는 -2147483648로 출력되는 것이다.
이를 방지하기 위해서는 정수값에 대한 검증을 해주는 게 좋으나, 이 문제에서는 그 부분까지 다룰 필요는 없는 것 같아서 건너뛰도록 하겠다.

다시 문제로 돌아와서, 그렇다면 로직을 어떻게 짜는 게 좋을까?
다른 부분은 조금만 생각하면 금방 풀 수 있을 것 같으니 손익분기점을 넘지 않는 부분 관련해서만 서술하도록 하겠다.

  1. while문을 노트북 대수 nint 타입의 최대값보다 작은 동안(n < Integer.MAX_VALUE)만 실행시키며 n의 값을 증가시킨다.
    여기서 최대값을 포함시키지 않는 이유는, 만약 최대값을 포함시키게 되면(n <= Integer.MAX_VALUE) 루프가 한 번 더 실행하여 n의 값이 범위를 벗어나 -2147483648이 되기 때문이다.
  2. 손익분기점이 존재하지 않는 경우 루프를 빠져나오게 되면 n의 값이 int의 최대값이 되므로 여기서 n-1로 조정해준다.

글만 읽으면 이해가 잘 안 갈수도 있다. 아래 코드를 보면 좀 더 쉽게 이해할 수 있을 것이다.

💻 코드

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
			
		long a = scanner.nextInt(); // 고정비용
		long b = scanner.nextInt(); // 가변비용
		long c = scanner.nextInt(); // 노트북 가격
		
		scanner.close();
		
		int n = 1;	// 노트북 대수
		
		// 손익분기점 = 총 수입(c*n) > 총 비용(a+(b*n))
		while(c*n <= a+(b*n) && n < Integer.MAX_VALUE)  // 손익분기점을 넘지 않고 정수 n이 범위값보다 작은 동안
			n++;					// 최대값도 포함시키면 오버플로우로 인해 -2147483648이 출력됨
		
		if(n == Integer.MAX_VALUE)  // 루프 빠져나왔을 때 노트북 대수가 정수의 최대값이라면(손익분기점 존재 x)
			n = -1;
		
		System.out.println(n);
	}
}
profile
기쁘게 코딩하고 싶은 백엔드 개발자

0개의 댓글