본문 바로가기

알고리즘/백준 문제 풀이

[Python] 2295번 세 수의 합

728x90

https://www.acmicpc.net/problem/2295

 

2295번: 세 수의 합

우리가 x번째 수, y번째 수, z번째 수를 더해서 k번째 수를 만들었다라고 하자. 위의 예제에서 2+3+5=10의 경우는 x, y, z, k가 차례로 1, 2, 3, 4가 되며, 최적해의 경우는 2, 3, 4, 5가 된다. k번째 수가 최

www.acmicpc.net


 

23/12/28

 

 

중간에서 만나기(MITM)으로 안 풀어도 집합으로 쉽게 풀 수 있는 문제이다.

 

나는 중간에서 만나기 알고리즘 연습을 위해 그 방법으로 문제를 해결했다.


 

첫번째 문제 접근 방식:

 

 

MITM으로 해결했다.

 

세 수의 합이 최대가 되도록 하려는 것이 우리의 목적이고, 그 세 수의 합 또한 기존 배열에 존재해야 한다.

 

나는 두 개로 분할했는데, 하나는 $x$번째 수와 $y$번째 수를 더한 합 배열, 그리고 $z$번째 수에서 $k$번째 수를 뺀 차 배열을 생각했다.

 

편의 상 전자를 $xy$배열, 후자를 $zk$배열이라고 하자.

 

$xy$배열이 정렬되어 있다고 하면, 우리는 $zk$배열의 원소에 음의 부호를 취해준 것, 즉, $k$번째 수에서 $z$번째 수를 뺀 원소 하나 당 조건을 만족시키는 $xy$배열 하나를 이분 탐색을 통해 찾아낼 수 있을 것이다.

 

즉, $U[x] + U[y] = U[k] - U[z]$를 만족시키는 $xy$배열의 원소 하나를 찾아낼 수 있다.

 

따라서 $\mathcal{O}(N^2\mathrm{log}N)$의 시간 복잡도로 문제를 해결할 수 있다.

 


아래는 내가 위의 접근 방식과 같이 작성한 파이썬 코드이다. 더보기를 누르면 확인할 수 있다.

더보기
# 2295번 세 수의 합
# 분할 정복
'''
접근 방법:
밋 인더 미들
'''
import sys
input = sys.stdin.readline

def bisect(num, li):
    start, end = 0, len(li)-1
    while start <= end:
        mid = (start+end)//2
        if li[mid] == num:
            return mid
        if li[mid] > num:
            end = mid-1
        else:
            start = mid+1
    if li[start] == num:
        return start
    return None

N = int(input())
U = list(int(input()) for _ in range(N))
num_li1 = [0 for _ in range(N*N)]
num_li2 = [0 for _ in range(N*N)]
for i in range(N):
    for j in range(N):
        num_li1[i*N+j] = U[i] + U[j]
        num_li2[i*N+j] = U[i] - U[j]
num_li1.sort()
max_val = 0
for z in range(N):
    for k in range(N):
        num = -num_li2[z*N+k]
        a = bisect(num, num_li1)
        if a != None and U[k] > max_val:
            max_val = U[k]
print(max_val)

 

두번째 문제 접근 방식:

 

 

$xy$배열을 만든다는 것 자체는 같지만, 정확히 말하면 배열이 아니라 집합으로 구현함으로써 시간을 더 줄일 수 있다.

 

있는지 없는지 확인하는 것은 상수의 시간이 걸리기 때문에, $\mathcal{O}(N^2)$의 시간 복잡도로 해결할 수 있다.

 


아래는 내가 위의 접근 방식과 같이 작성한 파이썬 코드이다. 더보기를 누르면 확인할 수 있다.

더보기
# 2295번 세 수의 합
# 이분 탐색
'''
접근 방법:
이분 탐색 + 집합
'''
import sys
input = sys.stdin.readline

N = int(input())
U = sorted(int(input()) for _ in range(N))
num_set = set(U)
num_li1 = set(i+j for i in U for j in U)
max_val = 0
for z in range(N):
    for k in range(z, N):
        if (U[k] - U[z]) in num_li1 and max_val < U[k]:
            max_val = U[k]
print(max_val)

'알고리즘 > 백준 문제 풀이' 카테고리의 다른 글

[Python] 17633번 제곱수의 합 (More Huge)  (0) 2024.01.04
[Python] 5526번 ダーツ (Darts)  (0) 2024.01.04
[Python] 9007번 카누 선수  (0) 2024.01.04
[Python] 11967번 불켜기  (0) 2024.01.04
[Python] 15573번 채굴  (0) 2024.01.03