오늘 풀어본 문제는 BOJ 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 의 값은 최대 100,000 이하이다. 투포인터를 사용하면 O(N) 으로 풀 수 있겠다 싶었다.
먼저 주어진 정수들을 오름 차순으로 정렬 시킨 뒤 두 포인터 변수 left
right
는 각각 0, N-1 에서 시작한다. 두 포인터가 가리키고 있는 변수를 더한 값이 0보다 작다면 left를 증가, 0보다 크다면 right를 감소 시킨다.
탐색 과정에서 두 수의 합이 최소라면 해당 시점에 두 포인터가 가리키는 변수를 차례대로 기록한다. left<right
가 유지되는 선에서 탐색을 진행한다.
left<right
조건이 유지되어야 하므로 N개의 정수를 한번씩만 탐색해서 정답을 구할 수 있었다!
하지만 동일한 방법으로 swift 로 풀어보았지만 시간초과가 발생했다 ㅜ 왜그런거지,, swift 로 한다면 log N 에 끝내야 한다는 소리인가,,?
이분 탐색 이용 방법은 잘 생각이 나지 않아 [BinarySearch] BOJ 2470 두 용액 포스팅을 참고하였다.
투포인터 사용시와 동일하게 주어진 숫자들을 오름차순으로 정렬한 뒤 i = 0~N-1
번째 정수를 기준으로 해당 숫자에 더해서 가장 최소의 절댓값을 갖는 수를 찾는 방식이다.
두 변수 left
, right
는 각각 i+1, n-1 에서 시작하고 mid = (left+right)/2
가 가리키는 정수와 i번째 정수의 합이 최소가 되는 지점을 찾아 나간다.
N개의 변수에 대해서 진행되고 각 변수마다 최적의 짝을 찾는 과정은 logN 의 시간을 가질테니 O(N log N)의 시간복잡도를 가지는 것 같다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace std;
pair<long, long> solution(vector<long> numbers){
pair<long, long> answer;
sort(numbers.begin(), numbers.end());
long min = 2147483647;
int left = 0, right = numbers.size()-1;
while (left<right){
int sum = numbers[left] + numbers[right];
if (min > abs(sum)){
min = abs(sum);
answer.first = numbers[left];
answer.second = numbers[right];
}
if (sum < 0) left++;
else right--;
}
return answer;
}
int main() {
int N;
cin>>N;
vector<long> numbers(N);
for (int i = 0; i < N; ++i) {
cin>>numbers[i];
}
pair<long, long> answer = solution(numbers);
cout<<answer.first<<' '<<answer.second;
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace std;
pair<long, long> solution(vector<long> numbers){
pair<long, long> answer;
sort(numbers.begin(), numbers.end());
long min = 2147483647;
for (int i = 0; i < numbers.size(); ++i) {
int left = i+1, right = numbers.size()-1;
while (left<=right){
int mid = (left+right)/2;
int sum = numbers[i] + numbers[mid];
if (abs(sum) < min){
min = abs(sum);
answer.first = numbers[i];
answer.second = numbers[mid];
}
if (sum<0) left = mid+1;
else right = mid-1;
}
}
return answer;
}
int main() {
int N;
cin>>N;
vector<long> numbers(N);
for (int i = 0; i < N; ++i) {
cin>>numbers[i];
}
pair<long, long> answer = solution(numbers);
cout<<answer.first<<' '<<answer.second;
return 0;
}