백준 - 2467번 용액(이분탐색)

Kiwoong Park·2023년 4월 16일
0

문제
KOI 부설 과학연구소에서는 많은 종류의 산성 용액과 알칼리성 용액을 보유하고 있다. 각 용액에는 그 용액의 특성을 나타내는 하나의 정수가 주어져있다. 산성 용액의 특성값은 1부터 1,000,000,000까지의 양의 정수로 나타내고, 알칼리성 용액의 특성값은 -1부터 -1,000,000,000까지의 음의 정수로 나타낸다.

같은 양의 두 용액을 혼합한 용액의 특성값은 혼합에 사용된 각 용액의 특성값의 합으로 정의한다. 이 연구소에서는 같은 양의 두 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들려고 한다.

예를 들어, 주어진 용액들의 특성값이 [-99, -2, -1, 4, 98]인 경우에는 특성값이 -99인 용액과 특성값이 98인 용액을 혼합하면 특성값이 -1인 용액을 만들 수 있고, 이 용액의 특성값이 0에 가장 가까운 용액이다. 참고로, 두 종류의 알칼리성 용액만으로나 혹은 두 종류의 산성 용액만으로 특성값이 0에 가장 가까운 혼합 용액을 만드는 경우도 존재할 수 있다.

산성 용액과 알칼리성 용액의 특성값이 정렬된 순서로 주어졌을 때, 이 중 두 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액을 찾는 프로그램을 작성하시오.

입력
첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 서로 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.

출력
첫째 줄에 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액의 특성값을 출력한다. 출력해야 하는 두 용액은 특성값의 오름차순으로 출력한다. 특성값이 0에 가장 가까운 용액을 만들어내는 경우가 두 개 이상일 경우에는 그 중 아무것이나 하나를 출력한다.

파이썬 풀이

첫번째 접근 방법 : 이분탐색이 아닌 양 끝을 합계 값에 따라 한 칸씩 줄여가면서 확인하는 방법 - 투 포인터(Two pointer) 알고리즘을 활용한 방법이라고 할 수 있다.

N, *arr = map(int, open(0).read().split())
s,e=0,N-1
mx = [2E9,0,0]                  # 최대값으로 초기화
while(s<e):
    tot=arr[s]+arr[e]           # 합계
    if abs(tot)<mx[0]:          # 최소값 갱신되는 경우
        mx = [abs(tot),arr[s],arr[e]]
    if tot<0: s+=1 	            # 새로운 최소값을 찾기 위해 합계가 음수면 가장 작은 값의 인덱스를 하나 늘림. 
    else: e-=1					# 합계가 양수면 가장 큰 값의 인덱스를 하나 줄임.
print(*mx[1:])

문제의도에 맞게 이분탐색 방법이나 투 포인터 방식보다 효율적이지는 않은 것 같다. 어차피 for문을 통해 최소값을 설정하고 나머지 값을 이분탐색 하는 방식이니 시간복잡도는 nlog(n)인 것 같아서..

N, *arr = map(int, open(0).read().split())
mx=2E9					# 최대값으로 초기화
l,r=0,0					# 정답으로 쓸 왼쪽, 오른쪽 인덱스 초기화
for i in range(N-1):	# 마지막부터 시작할 필요는 없으므로 0부터 N-1까지
    tmp=arr[i]          # 시작점의 값을 정하고, 나머지 리스트의 원소 중 이분탐색으로 찾는 방식
    s,e=i+1,N-1           
    while(s<=e):
        mid=(s+e)//2
        tot=tmp+arr[mid]
        if abs(tot) < mx:
            mx=abs(tot)
            l,r=i,mid
        if tot<0: s=mid+1
        else: e=mid-1
print(arr[l],arr[r])

C++ 풀이

#include <iostream>
#include<vector>
using namespace std;

int main()
{
    int N;
    cin >> N;
    vector<int> vec(N);
    
    for (int i=0;i<N;i++)
        cin >> vec[i];
    
    int s=0, e=N-1, sum=2E9,tot,a,b;
    /* 중요한 것은 while 조건에 '<='연산자를 넣으면 안된다는 것!
       반례를 들자면 만약 아래와 같이 
       2
       2 3 이라는 입력이 들어올 경우
       처음 a, b = 2, 3을 저장한 뒤
       아래 else 문을 통해 tot = 5로 0보다 크므로 
       e가 하나 작아져 0이 되고
       다시 while문을 통과하고 if 문을 통과해서
       a,b = 2, 2로 결과가 갱신된다.
    */
  
    while(s<e) {
        tot = vec[s]+vec[e];
        if (abs(tot) < sum) {
            a=vec[s];
            b=vec[e];
            sum=abs(tot);
        }
        if (tot<0)
            s++;
        else
            e--;
    }
    cout << a << " " << b;

    return 0;
}
profile
You matter, never give up

0개의 댓글

관련 채용 정보