코딩 테스트/백준
[Python] 백준 3273 두 수의 합
위시리
2024. 12. 23. 22:47
문제 분석
- n개의 양의 정수가 주어졌을 때
- 이 중 두 수를 더해 x를 만족하는 쌍의 개수 출력
코드 설계
- 완탐?
- 앞에서부터 하나 픽(a)하고 나머지 중에 (나머지인데 인덱스 값이 더 큰 값들)
- x - a 인 값이 있는지 확인
- 있으면 cnt += 1
정답 코드
1차 - 시간초과
import sys
input = sys.stdin.readline
n = int(input())
arr = sorted(list(map(int, input().split())))
x = int(input())
cnt = 0
for i in range(0, n-1) :
if arr[i] >= x :
continue
if (x - int(arr[i])) in arr[i+1:] :
cnt += 1
print(cnt)
다른 사람 코드 1 - 투 포인터
import sys
input = sys.stdin.readline
n = int(input())
numbers = sorted(list(map(int, input().split())))
x = int(input())
cnt = 0
left, right = 0, n-1
while left < right :
tmp = numbers[left] + numbers[right]
if tmp == x :
cnt += 1
left += 1
elif tmp < x :
left += 1
else :
right -= 1
print(cnt)
** 투포인터
- 두 수의 합이 x보다 큰 경우 - 더 큰 값을 더해야하므로 left+1
- 두 수의 합이 x보다 작은 경우 - 더 작은 값을 더해야하므로 right-1
- 두 수의 합이 x인 경우 - answer+1 & left+1
투 포인터 탐색은 왼쪽, 오른쪽 방향의 인덱스가 서로 만날 때(교차할 때)까지 진행
다른 사람 코드 2
import sys
n = int(sys.stdin.readline())
a = set(map(int, sys.stdin.readline().split(" ")))
x = int(sys.stdin.readline())
count = 0
for i in a:
j = x - i
if j in a:
count += 1
print(int(count/2))
다만 (1, 3) (3, 1) 과 같이 두번씩 카운트 되므로 i<j 를 체크해주거나 카운트를 절반으로 나누어주면 된다.
2회차 - 25.02.07
- 자연수 x가 주어졌을 때, 두 수의 합이 x가 되는 쌍의 수를 구하는 프로그램을 작성하라
- 쌍 → 순서 상관 x
- 제한시간 1초 : 연산 1억번 → 2중 for문을 돌면 시간초과
1차 - 시간 초과
import sys
input = sys.stdin.readline
n = int(input())
nums = list(map(int, input().split()))
x = int(input())
ans = 0
for n in nums :
if x-n in nums :
ans += 1
del nums[nums.index(n)]
del nums[nums.index(x-n)]
print(ans)
2차 - 시간 초과
정렬 → 이분탐색?
import sys
input = sys.stdin.readline
def isExist(n, arr) :
# 종료조건
if len(arr) == 1 and n not in arr :
return False
mid = len(arr) // 2
if n == arr[mid] :
return True
if n < arr[mid] :
return isExist(n, arr[:mid])
elif n >= arr[mid] :
return isExist(n, arr[mid:])
n = int(input())
nums = list(map(int, input().split()))
x = int(input())
nums = sorted(nums) # 정렬
ans = 0
for n in nums :
if isExist(x-n, nums) :
ans += 1
del nums[nums.index(n)]
del nums[nums.index(x-n)]
print(ans)
리펙토링..
import sys
input = sys.stdin.readline
# def isExist(n, arr) :
# # 종료조건
# if len(arr) == 1 and n not in arr :
# return False
#
# mid = len(arr) // 2
# if n == arr[mid] :
# return True
#
# if n < arr[mid] :
# return isExist(n, arr[:mid])
# elif n >= arr[mid] :
# return isExist(n, arr[mid:])
def isExist(n, arr):
if not arr: # 배열이 비었으면 False 반환
return False
mid = len(arr) // 2
if n == arr[mid]:
return True
return isExist(n, arr[:mid]) if n < arr[mid] else isExist(n, arr[mid:])
n = int(input())
nums = list(map(int, input().split()))
x = int(input())
nums = sorted(nums) # 정렬
ans = 0
for n in nums :
if isExist(x-n, nums) :
ans += 1
del nums[nums.index(n)]
del nums[nums.index(x-n)]
print(ans)
왜 시간초과가 날까?
nums = sorted(nums) # O(N log N)
→ 파이썬 정렬의 시간복잡도는 O(N log N)
for n in nums: # O(N)
if isExist(x - n, nums): # O(log N)
ans += 1
del nums[nums.index(n)] # O(N)
del nums[nums.index(x - n)] # O(N)
- for n in nums: → nums의 길이가 N이므로 최악의 경우 O(N) 번 반복
- isExist(x - n, nums) → 이진 탐색(O(log N)) 수행
- nums.index(n)와 nums.index(x - n) → 각각 O(N) (리스트에서 값 찾기)
- del nums[...] → 리스트 요소 삭제는 O(N)
투포인터 by chatGPT
nums.sort() # O(N log N)
left, right = 0, len(nums) - 1
ans = 0
while left < right:
s = nums[left] + nums[right]
if s == x:
ans += 1
left += 1
right -= 1
elif s < x:
left += 1
else:
right -= 1
✅ 정렬 후 O(N log N) + 투 포인터 O(N) → 최종 O(N log N)
✅ N이 100,000이어도 충분히 1초 안에 실행 가능!
3회차 - 25.02.14
문제 유형 : 투 포인터
import sys
input = sys.stdin.readline
n = int(input())
pro = sorted(list(map(int, input().split())))
x = int(input())
right = len(pro)-1
left = 0
ans = 0
while(left < right) :
if pro[left] + pro[right] == x :
ans += 1
left += 1
# right -= 1
elif pro[left] + pro[right] < x :
left += 1
else :
right -= 1
print(ans)