https://www.acmicpc.net/problem/11053
https://www.acmicpc.net/problem/12015
부분수열: 원래 수열에서 순서를 유지한 채 일부 원소를 선택한 수열 (연속할 필요는 없음)
증가수열: 각 원소가 이전 원소보다 크도록 선택된 부분 수열
즉 이 문제에서 이야기하는건 부분수열 + 증가수열 인데 그중 가장 긴 경우의 원소 갯수를 출력하란 의미인거다.
[1,2,3,2,3,4,5,6] 이 있다고 했을 때 여기선, [1,2,3,4,5,6] 으로 총 6이 답이 될 수 있다.
풀이 1 DP
DP를 전부 다 1로 초기화 한 다음에,
본인보다 위치도 작으면서, 키도 작은 값 들 중에 기존 DP 값이 가장 큰 경우 를 뽑아 +1 을 더해가며 DP 테이블을 채우는 방식이다.
이게 처음에는 바로 이전 원소랑만 비교하면 되지 않나(?) 생각했는데,
1,2,3,2,3,4,5,6 이런식으로 원소가 구성되어 있을 경우
[1,2,3,3,4,5,6,7] -> 이렇게 중간에 값이 오르락 내리락(?) 하는 경우 값을 제대로 갱신할 수 없다. 그래서 On^2 이지만 이전 dp값을 확실히 알 수 있도록 모든 원소를 다 돌아봐야 한다.
n = input()
arr = list(map(int, input().split()))
dp = [1 for _ in range(len(arr))]
ans = 0
cursor = 0
# 바텀업 On^2
for i in range(0,len(arr)):
mx = 0
for j in range(0, i):
# 이전 원소들 중에, 현재 본인보다 작은 값 이지만 증가수열이 가장 긴 부분 채택
if(arr[j] < arr[i]):
mx = max(dp[j], mx)
dp[i] = mx + 1
ans = max(dp[i],ans)
print(ans)
풀이2 DP + 이분탐색
위의 On^2 으로 돌아가는 코드의 경우 N이 1000개 이상일 경우 시간초과가 날 수 있다.
따라서, 다른 방법을 써 줘야 하는데 이 아이디어를 처음에는 이해를 못했던 것이, 위의 풀이를 보고 중간에 원소 채택하는 부분만 바꾸면 되는줄 알았다.
근데 그게 아니라, dp 테이블 내에서 이분탐색으로 새로운 원소가 들어갔을 때 어디 위치해야 하는지 결정해서 처리하는 것이었다.
예를 들면, [1,2,5,2,3] 라는 원소를 가진 테이블에서 dp 테이블을 갱신할 때
1) dp = [1,2] , 원소 = 5 이면 어짜피 5이 dp테이블 내 최종 값보다 크니까 추가해 주면 된다. [1,2,5]
2) dp = [1,2,5], 원소 = 2 이면, 2가 dp테이블 내 이미 두번째 인덱스에 있으므로 2를 교체해 주면 된다.
3) dp = [1,2,5], 원소 = 3 이면, **3이 위치할만한 곳은 5가 위치한 곳인데, 어짜피 증가하는 수열이기만 하면되니** 5를 교체
import sys
# from bisect import bisect_left
input = sys.stdin.readline
n = input()
arr = list(map(int, input().split()))
dp = []
ans = 0
cursor = 0
def bs_left(dp, target):
left = 0
# 중요 !! dp 내 삽입 위치 정하기
right = len(dp) - 1
while(left <= right):
mid = (left + right) // 2
if(dp[mid] < target):
left = mid + 1
else:
right = mid - 1
return left
for i in range(0,len(arr)):
# 현재 요소가 dp 내 어느 위치에 있어야 할지 반환
# 항상 dp의 끝부분은 가능한한 큰 수가 있으니까, 만약 그 수 보다 크다면 새로 덧붙이고
# 만약 그것보다 작다면 적합한 위치를 찾아 대체 (값도, 키도 둘다 오름차순 임을 전제로)
left = bs_left(dp, arr[i])
if(left == len(dp)):
dp.append(arr[i])
ans += 1
else:
dp[left] = arr[i]
# 2, 3, 2, 1 -> [1,3]
# 왜냐면 이게 어짜피 *증가*하는 수열의 *갯수*만 구하는 거니까 위치가 겹칠 경우 작은값이 덮어써도 상관없는거임
print(ans) #, dp)'알고리즘' 카테고리의 다른 글
| 원형큐, 큐, 덱 [백준 10866, 18258] (0) | 2024.12.23 |
|---|---|
| 플로이드 워셜 알고리즘 (1) | 2024.12.19 |
| 다익스트라 (0) | 2024.12.10 |
| [이코테] 효율적인 화폐 구성 (2) | 2024.12.09 |
| [이코테] 바닥공사 (1) | 2024.12.09 |