#2470 두 용액
난이도 : 골드 5
유형 : 투 포인터 / 파라메트릭 서치
▸ 문제
KOI 부설 과학연구소에서는 많은 종류의 산성 용액과 알칼리성 용액을 보유하고 있다. 각 용액에는 그 용액의 특성을 나타내는 하나의 정수가 주어져있다. 산성 용액의 특성값은 1부터 1,000,000,000까지의 양의 정수로 나타내고, 알칼리성 용액의 특성값은 -1부터 -1,000,000,000까지의 음의 정수로 나타낸다.
같은 양의 두 용액을 혼합한 용액의 특성값은 혼합에 사용된 각 용액의 특성값의 합으로 정의한다. 이 연구소에서는 같은 양의 두 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들려고 한다.
예를 들어, 주어진 용액들의 특성값이 [-2, 4, -99, -1, 98]인 경우에는 특성값이 -99인 용액과 특성값이 98인 용액을 혼합하면 특성값이 -1인 용액을 만들 수 있고, 이 용액이 특성값이 0에 가장 가까운 용액이다. 참고로, 두 종류의 알칼리성 용액만으로나 혹은 두 종류의 산성 용액만으로 특성값이 0에 가장 가까운 혼합 용액을 만드는 경우도 존재할 수 있다.
산성 용액과 알칼리성 용액의 특성값이 주어졌을 때, 이 중 두 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액을 찾는 프로그램을 작성하시오.
▸ 입력
첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.
▸ 출력
첫째 줄에 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액의 특성값을 출력한다. 출력해야 하는 두 용액은 특성값의 오름차순으로 출력한다. 특성값이 0에 가장 가까운 용액을 만들어내는 경우가 두 개 이상일 경우에는 그 중 아무것이나 하나를 출력한다.
문제 풀이
해당 문제는 투 포인터 기법으로 풀이해주면 된다. 투 포인터는 주어진 배열을 두 개의 포인터를 조작하면서 원하는 것을 얻는 기법이다.
백준 2003번 수들의 합2의 문제는 두 개의 포인터가 같은 방향으로 탐색을 진행했다면 해당 문제는 각 양 끝에서 가운데 방향으로 탐색을 해줄 것이다.
파라메트릭 서치
주어진 배열에서 0에 가장 가까운 용액을 만들어 내는 추출해줘야 하기 때문에 먼저 배열을 정렬해준 후, 양쪽 끝에서 두 포인터를 이용하여 서로 중앙을 향해 거리를 좁혀지는 형태로 진행하면서 용액을 골라내주면 된다. 두 개 이상일 경우에는 아무거나 추출해주면 된다. 정렬해주는 이유는 두 용액의 합이 0보다 클 때는 합을 줄여줘야 하기 때문에 오른쪽에 위치한 포인터를 움직여주고, 0보다 작을 때는 합을 크게해줘야 하기 때문에 왼쪽의 포인터를 움직이게 하기 위해서이다.
주어진 범위 내에 원하는 값 또는 조건에 일치하는 값을 찾아야하므로 이분 탐색이 아닌 최적화 문제를 결정 문제로 바꿔 '파라메트릭 서치'를 사용할 것이다.
- 최적화 문제 : 두 개의 서로 다른 용액을 혼합하여 특성값이 최소인 용액은 얼마인가?
- 결정 문제 : 두 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들 수 있는가?
포인터 2개가 양끝에서 시작하여 반대로 진행
해당 배열 arr [-1, 4, 7, -99, -2]로 시뮬레이션을 돌려보자.
빨간색은 s이고, 파란색은 e인 두 포인터는 s는 맨 왼쪽 0에서 시작하고 e는 배열의 끝 n에서 탐색을 시작한다. arr[s]+arr[e]의 값을 구한 다음 0과 가까운 값을 구하면 된다.
arr[s]+arr[e] = -92 < 0 이므로 값이 더 커져야한다. 해당 배열은 오름차순으로 정렬했기 때문에 두 수의 합이 커지기 위해서 s의 포인터를 한 칸 움직여줘서 더 큰 값을 넣어준다.
arr[s]+arr[e] = 5 > 0 이므로 값이 더 작아져야 한다. 작은 값을 넣어주기 위해서는 e의 포인터를 움직여준다.
arr[s]+arr[e] = 2 > 0 이므로 값이 더 작아져야 하므로 위와 같이 e의 포인터를 움직여준다.
arr[s]+arr[e] = -3 < 0 이므로 이번엔 s를 움직여주자.
그런데 start >= end이므로 더 이상 탐색이 불가능하므로 종료해준다.
☛ 답은 가장 0에 가까웠던 2로, 해당 구간에 속하는 두 용액 -2, 4가 된다.
풀이 코드
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
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());
}
int[] res = new int[2];
int s=0, e=n-1, min=Integer.MAX_VALUE;
Arrays.sort(arr);
while(s < e) {
int sum = arr[s]+arr[e];
if(min> Math.abs(sum)) {
min = Math.abs(sum);
res[0] = arr[s];
res[1] = arr[e];
if(sum==0) break;
}
if(sum <0) s++;
else e--;
}
System.out.println(res[0]+" "+res[1]);
}
}
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 10999번 구간 합 구하기2 (Java) (0) | 2021.07.08 |
---|---|
[BOJ] 백준 11003번 최솟값 찾기 (Java) (0) | 2021.07.07 |
[BOJ] 백준 2003번 수들의 합2 (Java) (0) | 2021.07.06 |
[BOJ] 백준 2075번 N번째 큰 수 (Java) (0) | 2021.07.05 |
[BOJ] 백준 9935번 문자열 폭발 (Java) (1) | 2021.07.04 |