새소식

Computer Science/코딩테스트 문제 풀이

[백준] [파이썬] [그리디] [투 포인터] 2230번: 수 고르기

  • -

 

투 포인터 문제집 : https://www.acmicpc.net/workbook/view/8709

 

문제: https://www.acmicpc.net/problem/2230

 

문제

N개의 정수로 이루어진 수열 A[1], A[2], …, A[N]이 있다. 이 수열에서 두 수를 골랐을 때(같은 수일 수도 있다), 그 차이가 M 이상이면서 제일 작은 경우를 구하는 프로그램을 작성하시오.

예를 들어 수열이 {1, 2, 3, 4, 5}라고 하자. 만약 M = 3일 경우, 1 4, 1 5, 2 5를 골랐을 때 그 차이가 M 이상이 된다. 이 중에서 차이가 가장 작은 경우는 1 4나 2 5를 골랐을 때의 3이 된다.

입력

첫째 줄에 두 정수 N, M이 주어진다. 다음 N개의 줄에는 차례로 A[1], A[2], …, A[N]이 주어진다.

출력

첫째 줄에 M 이상이면서 가장 작은 차이를 출력한다. 항상 차이가 M이상인 두 수를 고를 수 있다.

제한

  • 1 ≤ N ≤ 100,000
  • 0 ≤ M ≤ 2,000,000,000
  • 0 ≤ |A[i]| ≤ 1,000,000,000

예제 입력 1 

3 3
1
5
3

예제 출력 1 

4

 

 

문제 요약 

목표: M 이상이면서 가장 작은 차이를 출력한다. 

  • [1, 5, 3] 에서 두 숫자를 골라 차이를 구하는 모든 경우의 수는 다음과 같다.
    • (1, 5) : 5 - 1 = 4
    • (1, 3) : 3 - 1 = 2
    • (5, 3) : 5 - 3 = 2
    • (1, 1), (3, 3), (5, 5) : 0
  • 이 중 문제 예시에서 주어진 M = 3이상이면서, 차이가 가장 작은 경우는 4이다.
  • 이렇게 주어진 수열에서 구할 수 있는 M 이상의 차이 값 중 최소값을 구해 출력하면 된다.

 

우선 정렬을 해야한다. 

예시
A = [5, 3, 9, 10, 1]
M = 3

 

정렬을 안했을 때

5를 기준으로 나머지 값들과 비교해가면서 차이를 구한다고 했을 때

  • 5 - 5 = 0
  • 5 - 3 = 2
  • 5 - 9 = -4
  • 5 - 12 = -7
  • 5 - 1 = 4

이렇게 나오는 값이 뒤죽 박죽이라 모든 경우의 수를 탐색해야하고 의 탐색을 거쳐야 한다.
또한 결과가 양수일지 음수일지 알 수 없어 abs()를 씌워줘야 한다.

 

정렬을 했을 때

수열을 정렬한다면 [1, 3, 5, 9, 12]의 형태가 되는데, 1을 고정하고 나머지 수와 차이를 구할 때

  • 1 - 1 = 0
  • 3 - 1 = 2
  • 5 - 1 = 4 -> 4 저장

여기까지만 계산하면 된다.
왜냐하면 이미 주어진 M = 3보다 차이가 커진 상황이고, 배열이 오름차순 정렬되어있기 때문에 뒤의 수를 탐색해 봤자 차이가 더 커질 것이기 때문이다.

현재 찾은 'M보다 큰 최소값'인 4를 저장하고 다음 순서로 넘어간다.

 

'1' 다음 위치인 '3'에서 마지막 탐색시점부터 탐색하면 된다.

  • 5 - 3 = 2
  • 9 - 3 = 6

6이 M보다 크기 때문에 더 이상 확인할 필요 없이 다음 순서로 넘어간다.
이 때, 기존에 저장된 정답인 4보다 작다면 갱신해주는 절차가 필요하다.

 

def solution(A, N, M):
    A.sort()  # 수열을 오름차순으로 정렬
    left = 0
    right = 1
    answer  = float('inf')  # 가장 작은 차이를 무한대로 초기화

    while right < N:
        diff = A[right] - A[left]
        
        if diff > M:
            answer  = min(answer , diff)
            left += 1  # 차이가 M 이상이면 더 작은 차이를 찾기 위해 left를 오른쪽으로 이동
        elif diff < M:
            right += 1  # 차이가 M 미만이면 right를 더 오른쪽으로 이동하여 차이를 키움
        else:           # diff == M 가장 이상적 더 이상 할 필요도 없다.
            return M

    return answer 

if __name__ == "__main__":
    N, M = map(int, input().split())

    A = [int(input()) for _ in range(N)]

    # 답변 구하기
    answer = solution(A, N, M)

    print(answer)

 

간단하게 생각해보는거다.

1. 정렬을 하고 

2. 왼쪽 화살표, 오른쪽 화살표 만들고 

3. 오른쪽 화살표가 범위 N을 넘어가기 전까지 진행해본다. 

4.1. 각 화살표가 가르키는 수의 차이가 목표 값 M보다 크면 기존 저장된 최소 차 diff랑 비교해서 갱신 

4.2 차이가 목표 값 M보다 작으면 조건 만족 안되므로 오른쪽 화살표 오른쪽으로 한칸 이동 

4.3 만약 차이가 딱 목표값인 M이 있으면 더 볼 필요도 없이 M을 출력하면 된다.  

목표: M 이상이면서 가장 작은 차이를 출력한다. 

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.