세 가지 용액을 반복문으로 돌리면 O(N^3)으로 시간초과가 발생한다.
하지만 투포인터
를 이용한다면 O(N^2)
으로 해결할 수 있다.
특정 용액(idx)
, 각 두 용액(left, right)
를 이용한다.
풀이 방법은 아래와 같다.
sum = arr[idx] + arr[left] + arr[right]
의 합을 통해 left와 right를 조절한다.sum > 0
이면 합을 줄이기 위해 right--
sum < 0
이면 합을 늘리기 위해 left++
sum의 절대값과 이전 합의 절대값과 비교
하여 sum의 절대값이 더 0에 수렴한다면 합의 최소값을 갱신하고, 세 용액의 특성 값을 저장한다.sum == 0이라면 더 이상 비교할 필요가 없기 때문에 갱신된 값을 반환한다.
주의할 점은 세 용액이 같은 용액으로 합을 구할 수 없고
, 세 용액의 합은 최대 |30억|이기 때문에 long타입
을 사용해줘야한다는 점이다.
public class 세용액 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int N = Integer.parseInt(br.readLine());
int[] arr = new int[N];
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
for (int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
//용액 오름차순 정렬
Arrays.sort(arr);
long result = Long.MAX_VALUE;
int[] answer = new int[3];
//특정 용액
for (int i = 0; i < N; i++) {
//투포인터를 이용한 나머지 두 용액
int left = 0;
int right = N - 1;
//투포인터를 이용한 두 용액이 사용했던 용액은 중복해서 확인할 필요가 없다.
while(left<N && right>0 && left<right){
//같은 용액으로 합을 구할 수 없다.
if(i == left){
left++;
continue;
}
//같은 용액으로 합을 구할 수 없다.
if(i == right){
right--;
continue;
}
//세 용액의 합
long sum = (long)arr[i]+arr[left]+arr[right];
//세 용액의 합이 더 0에 수렴한다면 합과 세 용액 갱신
if(result>Math.abs(sum)){
result = Math.abs(sum);
answer[0] = arr[i];
answer[1] = arr[left];
answer[2] = arr[right];
}
//세 용액의 합이 0이라면 더이상 비교할 필요가 없다.
if(sum == 0){
break;
}
//합이 0보다 크면 합의 크기를 줄여준다.
if(sum>0) right--;
//합이 0보다 작다면 합의 크기를 늘려준다.
if(sum<0) left++;
}
//세 용액의 합이 0이라면 더이상 비교할 필요가 없다.
if(result == 0) break;
}
//세 용액 사전순 정렬
Arrays.sort(answer);
StringBuilder sb = new StringBuilder();
for(int n : answer){
sb.append(n);
sb.append(" ");
}
bw.write(sb.toString());
bw.flush();
bw.close();
}
}
투포인터로 접근해야겠다는 생각은 했지만 접근하는데 있어 부족한점이 있었다.
(두용액의 합의 배열을 만들어 N^2크기의 배열과 N크기의 배열을 이용해 투포인터로 접근하려고 했다.)