주사위 면의 수 f인 (1...f) d 개의 주사위를 굴려 target을 만드는 경우의 수를 구하시오.
문제 및 예제의 원본이다.
문제를 딱 보고 든 풀 수 있는 방법 중 하나는 DFS, BFS 였다. 하지만 DFS, BFS로 하면 30면 짜리 주사위의 30개를 굴려서 특정 target이 될 수 있는 경우의 수를 구할 때 시간초과(TLE)가 날 수 있다.
그렇다면 다른 방법으로 풀 수 있는 게 무엇일까 생각해봤다.
DP 방법의 풀이가 가능한 지 알아보자. 일단 2차식 형태의 점화식을 세워보자. (여기서 2차식이라 함은 이차원 배열을 사용한다고 생각하면 된다.)
dp[d][k]를 주사위 d개로 t를 만들 수 있는 경우의 수라고 하자. (항상 dp에서는 점화식을 어떻게 정의하느냐가 중요하다.)
dp[d][t] = dp[d-1][1] + dp[d-1][2] + ... + dp[d-1][t-1]로 표현할 수 있고, 자세히 설명하자면 d-1 개의 주사위를 활용해서 1을 만드는 경우의 수에 나머지 한 개의 주사위에서 t-1이 되는 경우의 수.
따라서 dp[d-1][1]은 실질적으로 dp[d-1][1] * 1(1은 주사위에서 t-1이 나오는 경우의 수) 이다.
class Solution:
def numRollsToTarget(self, d: int, f: int, target: int) -> int:
# optimized solution
# Using DP Knapsack
dp = [[0 for _ in range(dd * f + 1)] for dd in range(d + 1)]
if target == 0 or target > d * f:
return 0
for i in range(1, f + 1):
dp[1][i] = 1
MOD = 10 ** 9 + 7
for i in range(2, d + 1):
for j in range(1, target + 1):
for k in range(1, f + 1):
if 0 <= j - k < len(dp[i - 1]):
dp[i][j] += dp[i - 1][j - k]
return dp[d][target] % MOD
def numRollsToTarget2(self, d:int, f:int, target: int) ->int:
dp = [[0 for _ in range(dd * f + 1)] for dd in range(d + 1)]
if target == 0 or target > d * f:
return 0
for i in range(1, f + 1):
dp[1][i] = 1
MOD = 10 ** 9 + 7
for i in range(2, d + 1):
for j in range(i, d * f + 1):
for k in range(1, f + 1):
if 0 <= j - k < len(dp[i - 1]):
dp[i][j] += dp[i - 1][j - k]
return dp[d][target] % MOD
두 번째 풀이가 내가 푼 방법인데, 시간이 오래 걸린다. 왜냐면 j 의 범위를 i 부터 d * f +1 까지로 했기 때문에 target 보다 큰 부분도 살피기 때문에 연산시간이 오래 걸린다. 이를 줄이려면 첫 번째 풀이처럼 하는 것을 추천한다.
'Python > DSA' 카테고리의 다른 글
[Union-Find] Friend-Circle (0) | 2020.10.05 |
---|---|
rotate image (0) | 2020.09.05 |
Longest Increment Subsequence (0) | 2020.09.03 |
Permutation(순열) 알고리즘 (0) | 2020.08.29 |
댓글