N개의 실수가 있을 때, 한 개 이상의 연속된 수들의 곱이 최대가 되는 부분을 찾아, 그 곱을 출력하는 프로그램을 작성하시오. 예를 들어 아래와 같이 8개의 양의 실수가 주어진다면,
색칠된 부분의 곱이 최대가 되며, 그 값은 1.638이다.
첫째 줄은 나열된 양의 실수들의 개수 N이 주어지고, 그 다음 줄부터 N개의 수가 한 줄에 하나씩 들어 있다. N은 10,000 이하의 자연수이다. 실수는 소수점 첫째자리까지 주어지며, 0.0보다 크거나 같고, 9.9보다 작거나 같다.
계산된 최댓값을 소수점 이하 넷째 자리에서 반올림하여 소수점 이하 셋째 자리까지 출력한다.
DP를 사용하는 문제는 모두 어떤 값
을 DP에 저장하는지에 따라 성능이 크게 달라진다.
이번 문제의 DP는 한번만 갱신하며, 각 DP는 이전 DP 값과 현재 수를 곱한 뒤 현재 수와 곱셈의 결과 중 큰 값을 저장한다.
처음 접근한 아이디어는 시간의 아이디어였다.
번째로 갱신하는 DP[j]
의 값은 번째 숫자부터 번째 숫자까지 개의 숫자를 곱한 값을 저장하려고 했다.
double[] numbers;
// numbers 입력
double[] DP = numbers;
//...//
위와 같은 코드를 작성했었는데, 결과가 제대로 나오지 않았다.
테스트케이스의 결과값이 70이상의 수가 나와서 이상하게 생각했는데, 디버깅을 해 보니 numbers
와 DP
가 같은 메모리 공간을 가리키게 되면서 DP
값을 수정하면 numbers
까지 같이 수정이 되는 오류가 있었다.
얕은 복사는 클래스 단위로만 이루어 지는 줄 알았기 때문에 일어난 실수다.
이번에는 50%에서 다시 틀렸다고 오류가 났다.
논리적으로는 오류가 없어서 한참을 고민했는데, 질문 글에서 힌트를 찾았다.
4
6.1
4.3
0.5
6.1
위의 입력 에서는 80.0015라는 계산 결과가 나오면서 80.002라는 출력이 나와야 했는데, 내 코드는 80.00149999999998 라는 계산 결과가 나오면서 80.001을 출력했다.
디버깅을 해 보니 6.1 * 4.3을 26.229999999999997로 계산하면서 오류가 발생한 것이다.
그래도 50% 실패가 해결되지 않았다.
문제가 해결되지 않아 다른 사람의 풀이를 찾아보니 시간으로 해결할 수 있는 훨씬 빠른 풀이가 존재했다.
해당 풀이 방법으로 다시 구현하여 성공할 수 있었다.
정확히 어떤 부분에서 오류가 났는지 확인할 수는 없었지만, 연산 횟수가 줄어듬에 따라 실수의 연산에서 나타날 수 있는 오차 범위가 줄었다고 예상한다.
import java.util.Scanner;
public class BJ2670 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
double[] numbers = new double[n];
for(int i = 0; i<n; i++) {
numbers[i] = sc.nextDouble();
}
double[] DP = new double[n];
for(int i = 0; i<n; i++)
DP[i] = numbers[i];
double max = DP[0];
for(int i = 1; i<n; i++){
double mult = DP[i - 1] * numbers[i];
if(mult > numbers[i])
DP[i] = mult;
}
for(int i = 0; i<n; i++)
if(max < DP[i])
max = DP[i];
max = Math.round(max*1000)/1000.0;
System.out.printf("%.3f",max);
}
}