문제 링크
https://www.acmicpc.net/problem/2470
2470번: 두 용액
첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -1,000,000,000 이상 1,000,00
www.acmicpc.net
문제 설명
N개의 숫자를 무작위 순서로 입력 받는다.
각 숫자의 범위는 -1,000,000,000 ~ 1,000,000,000 이며, N개의 숫자들 중 2개의 숫자를 선택했을 때
그 합의 절댓값이 0과 가장 가까운 두 수를 출력한다.
사고 과정
처음에는 이분 탐색으로 풀고자 하였다.
하지만 기존 이분 탐색 문제들에서는 하나의 값에 대해 탐색하는 과정이 필요했지만,
해당 문제의 경우 두 개의 값을 탐색해야 했다.
따라서 투 포인터를 사용하여 두 수의 합이 0에 가까운 값을 찾아야겠다는 생각이 들었다.
입력 받은 값들을 배열에 오름차순으로 정렬하고 인덱스를 활용하여 값을 찾고자 하였다.
Left와 Right 변수를 설정하여 두 수의 합에 따라 Left와 Right를 옮기고자 하였고,
두 수의 합이 이전의 합에 비해 작을 경우 출력될 결과값을 저장하고 있는 변수를 갱신하였다.
가장 어려웠던 점은 Left와 Right 변수를 어떻게 옮기느냐였다.
두 수의 합이 이전에 구했던 합보다 작다면 결과값을 갱신한 후에
Left를 증가시켜야 할 지 아니면 Right를 감소시켜야 할지,
이전에 구했던 합보다 크다면 결과값은 그대로 유지한 채
Left를 증가시켜야 하는지 Right를 감소시켜야 하는지 판단하는 것이 어려웠다.
해결 방법
단순하게 결과값 갱신 여부에 따라 Left 및 Right 변수를 수정해서는 안된다는 것을 깨달았다.
위 그림에서 temp는 현재 left와 right 인덱스에 해당하는 배열의 값을 합친 값을 의미하며
sum은 '현재까지' 두 수의 합을 구한 값들 중 절댓값이 최소인 값을 의미한다.
위에서 확인할 수 있듯이 sum 변수의 갱신 여부와 상관없이,
temp, 즉 현재 left와 right에 해당하는 배열의 값들을 합친 값이 음수인지 양수인지에 따라
left와 right 변수의 변경 방향이 결정된다.
left는 배열의 가장 왼쪽 인덱스를 (0), right는 배열의 가장 오른쪽 인덱스를 (N-1) 초기값으로 가지므로
temp값이 양수라면 right를 감소시킴으로써 다음 temp 값을 감소시켜야 할 것이며,
temp값이 음수라면 left를 증가시킴으로써 다음 temp 값을 증가시켜야 할 것이다.
해당 문제에서는 '서로 다른' 두 수의 합이 0과 가장 가까운 두 수를 구해야 하므로
left와 right가 같아지는 순간 반복문을 종료하게 된다.
추가적으로 현재 두 수의 합이 sum보다 0에 가까운지를 판단하는 함수를 작성해야 했는데,
절댓값으로서 판단해야 하기 때문에 sum과 temp가 양수인지, 혹은 음수인지에 따라 조건을 나누어 판단하였다.
코드
#include <iostream>
#include <algorithm>
using namespace std;
int N;
int arr[100001];
void init();
void input();
bool isNear(int left, int right, int sum);
void solve();
void init()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
}
void input()
{
cin >> N;
for (int i = 0; i < N; i++)
{
cin >> arr[i];
}
}
bool isNear(int left, int right, int sum)
{
int temp = arr[left] + arr[right];
if (temp >= 0)
{
if (sum >= 0)
{
if (sum > temp)
return true;
else
return false;
}
else
{
if (sum * -1 > temp)
return true;
else
return false;
}
}
else
{
if (sum >= 0)
{
if (sum > temp * -1)
return true;
else
return false;
}
else
{
if (sum * -1 > temp * -1)
return true;
else
return false;
}
}
}
void solve()
{
sort(arr, arr + N);
int left = 0;
int right = N - 1;
int sum = 2000000001;
int r1, r2;
while (left < right)
{
if (isNear(left, right, sum))
{
r1 = arr[left];
r2 = arr[right];
sum = arr[left] + arr[right];
}
if (arr[left] + arr[right] > 0)
{
right--;
}
else if (arr[left] + arr[right] == 0)
{
cout << arr[left] << " " << arr[right];
return;
}
else
{
left++;
}
}
cout << r1 << " " << r2;
}
int main()
{
init();
input();
solve();
return 0;
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 17298번 오큰수 (C++) (0) | 2022.07.02 |
---|---|
[백준] 1300번 K번째 수 (C++) (0) | 2022.06.27 |
[백준] 3079번 입국심사 (C++) (0) | 2022.06.27 |
[백준] 3020번 개똥벌레 (C++) (0) | 2022.06.24 |
[백준] 2110번 공유기 설치 (C++) (0) | 2022.06.20 |
댓글