728x90
https://www.acmicpc.net/problem/21870
24/11/04
단순한 분할정복 문제다.
문제 접근 방식:
얼핏 보면 시간제한이 1초고, 나이브하게 돌리면 빡빡할 것이라고 생각이 들 것이다.
우리는 왼쪽과 오른쪽으로 항상 배열이 나뉨을 관찰할 수 있다.
왼쪽 배열의 값들의 gcd + 오른쪽 배열의 최대 혹은 오른쪽 배열의 값들의 gcd + 왼쪽 배열의 최대 중 가장 큰 값이 우리가 원하는 값임을 확인할 수 있다.
오른쪽 배열의 최대 혹은 왼쪽 배열의 최대 또한 분할 정복을 통해 계속 재귀적으로 구할 수 있으므로, $\mathcal{O}(N \log N)$만에 문제를 해결할 수 있다.
DP로 값을 저장하지 않아도 충분히 돌아감을 확인할 수 있다.(오히려 값을 저장하면 저장하는 시간때문에 더 느리다.)
아래는 내가 위의 접근 방식과 같이 작성한 파이썬 코드이다. 더보기를 누르면 확인할 수 있다.
더보기
# 21870번 시철이가 사랑한 GCD
# DP
'''
접근 방법:
DP[total] = max(gcd(left) + DP[right], gcd(right) + DP[left])
근데 gcd(left)나 gcd(right)을 어떻게 빠르게 구할까?
gcd(left_left, left_right) 각각의 left_left, left_right은 저장
'''
import sys
input = sys.stdin.readline
from math import gcd
gcds = dict()
DP = dict()
N = int(input())
A = list(map(int, input().split()))
def find_gcd(si, ei):
if (si, ei) in gcds:
return gcds[(si, ei)]
if si == ei:
gcds[(si, ei)] = A[si]
return gcds[(si, ei)]
else:
S = ei - si + 1
left = find_gcd(si, si+S//2-1)
right = find_gcd(si+S//2, ei)
gcds[(si, ei)] = gcd(left, right)
return gcds[(si, ei)]
def dp(si, ei):
if (si, ei) in DP:
return DP[(si, ei)]
if si == ei:
DP[(si, ei)] = A[si]
return DP[(si, ei)]
S = ei - si + 1
left = dp(si, si+S//2-1)
right = dp(si+S//2, ei)
DP[(si, ei)] = max(right + find_gcd(si, si+S//2-1), left + find_gcd(si+S//2, ei))
return DP[(si, ei)]
ans = dp(0, N-1)
print(ans)
'알고리즘 > 백준 문제 풀이' 카테고리의 다른 글
[Python] 11728번 배열 합치기 (2) | 2024.11.25 |
---|---|
[Python] 9246번 다트판 (0) | 2024.11.24 |
[Python] 30855번 Fraction (0) | 2024.11.22 |
[Python] 4185번 Colliding Traffic (0) | 2024.11.21 |
[Python] 32653번 흑백 요리사 (0) | 2024.11.20 |