Notion에서 작성한 글이라, 여기에서 더 깔끔하게 보실 수 있습니다! 😮😊
problems
에 대하여 cost
당 각 문제를 풀 때 얻게 되는 alp_rwd
, cop_rwd
가 높은 순으로 그리디하게 풀어나간다고 했을 때alp
, cop
를 올리는 게 더 좋은 상황을 고려해주어야 한다.cost
전체를 들여야 능력치를 얻을 수 있다.alp
, cop
에 근접해갈 경우 cost
당 얻는 능력치가 높은 순이 아닌 경사하강법과 유사한 느낌으로 cost
가 상대적으로 덜 들면서 필요한 만큼만 능력치를 얻는 게 이득인 경우 또한 고려해주어야 한다.Coin DP
problems
를 순회하면서 현재의 alp
, cop
로 풀 수 있는 문제들의 alp_rwd
, cop_rwd
를 꼴로 저장해 놓는다. 이는 Coin DP 문제에서 현재 금액보다 작은 단위의 동전들에 대해 dp table을 갱신하는 것과 유사하다.alp
, cop
부터 시작해서 모든 rwd
에 대해 고려하면서 다음으로 풀 수 있는 문제들까지 걸리는 시간을 계산한다.alp_req
또는 cop_req
를 오름차순으로 정렬해서도 안 되고(cost
를 고려해주어야 함), … 아무튼 이 또한 현재의 선택이 전체 최적해로 이어짐을 보장하지 않는다.0-1 Knapsack DP
를 초기 알고력 , 초기 코딩력 에서 시작하여 알고력 , 코딩력 을 얻기까지의 최단시간이라고 하자.
의 크기는 모든 문제들을 풀 수 있는 알고력과 코딩력의 값으로 잡았다. 이는 이다.
먼저, 문제풀이로 알고력 또는 코딩력을 높이는 것이 아닌 시간당 1씩 증가하는 알고리즘 공부 또는 코딩 공부만 하는 상황으로 를 초기화할 수 있다.
초기 알고력 , 초기 코딩력 에서 시작하여, 공부하는 것으로 알고력 , 코딩력 를 얻기까지의 최단시간을 계속해서 갱신한다. python에서, 이는 다음과 같은 점화식으로 나타낼 수 있다.
그리고, 그 때의 알고력 , 코딩력 로 풀 수 있는 모든 문제들에 대해 풀면서 얻은 알고력(과 코딩력(을 얻기까지 걸린 시간을 갱신해준다.
이 때, 가 dp table의 범위를 넘어설 수 있으므로, 로 설정하고, 같은 이유로 또한 로 설정한다. 이를 각각 , 라 하자. 이는 다음 점화식으로 나타낼 수 있다.
def solution(alp, cop, problems):
alp_goal = max(max(p[0] for p in problems), alp)
cop_goal = max(max(p[1] for p in problems), cop)
dp = [[max(i-alp, 0)+max(j-cop, 0) for j in range(cop_goal+1)] for i in range(alp_goal+1)]
for i in range(alp, alp_goal+1):
for j in range(cop, cop_goal+1):
dp[i][j] = min(dp[i][j], dp[i-1][j]+1, dp[i][j-1]+1)
for ri, rj, di, dj, cost in problems:
if i >= ri and j >= rj:
ni, nj = min(i+di, alp_goal), min(j+dj, cop_goal)
dp[ni][nj] = min(dp[ni][nj], dp[i][j]+cost)
return dp[-1][-1]
from heapq import heappop, heappush
def solution(alp, cop, problems):
mx_alp, mx_cop = alp, cop
for p in problems:
if p[0] > mx_alp: mx_alp = p[0]
if p[1] > mx_cop: mx_cop = p[1]
dp = {}
dp[(alp, cop)] = 0
heap = [(0, alp, cop)]
while heap:
t, i, j = heappop(heap)
if i >= mx_alp and j >= mx_cop: return t
if dp.get((i, j), float('inf')) < t: continue
for ri, rj, di, dj, cost in problems:
if i >= ri and j >= rj:
ni = min(i+di, mx_alp)
nj = min(j+dj, mx_cop)
nt = t+cost
if dp.get((ni, nj), float('inf')) > nt:
dp[(ni, nj)] = nt
heappush(heap, (nt, ni, nj))
if i < mx_alp:
ni = i+1
nt = t+1
if dp.get((ni, j), float('inf')) > nt:
dp[(ni, j)] = nt
heappush(heap, (nt, ni, j))
if j < mx_cop:
nj = j+1
nt = t+1
if dp.get((i, nj), float('inf')) > nt:
dp[(i, nj)] = nt
heappush(heap, (nt, i, nj))
return dp[(mx_alp, mx_cop)]
if dp.get((i, j), float('inf')) < t: continue
구문을 통해 아래의 구문들을 실행하지 않게 되기 때문에 이 또한 주요 최적화 포인트이다. 이 구문이 실행될 때마다 의 시간복잡도가 save되는 셈이다.Memo
99클럽 3기 회고 를 내일중으로 작성하려고 한다.
혹시나 이 글을 봐주시는 분이 계시다면, 그동안 정말 감사했습니다 🥹😊
마늘맨님 응원합니다!