【Leetcode】2585-1910
前言
一眼DP的问题,作为学习而言,可以思考递归或者说记忆化搜索和正向DP两个角度出发解决问题
思路
1. 递归
递归的想法就十分的简单了
\(dfs(i,cur)=\sum_{k=0}^{cnt}{dfs(i-1,cur-k*v})\)
\(dfs(-1,0)=1 \text{ if }cur=0\)
\(dfs(-1,cur)=0 \text{ if }cur\neq0\)
其中,dfs(i,cur)表示从0~n索引的数组搜索到cur可以的表示方法。其递归等式的含义就是,当前长度的数组得到cur,可以由从上一个位置选取不超过最大个数k个分数组合而成。
临界条件是搜索到了数组最开始的地方,此时如果搜索到的cur是0,则表示有一个结果,即全部都不选。而此时如果cur!=0,则返回0。
据此写出代码
class Solution:
def waysToReachTarget(self, target: int, types: List[List[int]]) -> int:
MOD = 10 ** 9 + 7
n = len(types)
@cache
def dfs(i,cur):
if cur>target:return 0
if i==n:
if cur==target:
return 1
return 0
ans = 0
for k in range(types[i][0]+1):
ans = (ans+dfs(i+1,types[i][1]*k+cur))%MOD
return ans
return dfs(0,0)
代码的实现角度是从头出发进行搜索的,含义是一样的。
2. DP
dp[i][j]表示遍历到索引为i时,可以得到分数j的方案组合
\(dp[i+1][j]=\sum_{k=0}^{cnt}{dp[i][j-k*v]\text{ if }(j-k*v)\geq 0\text{ else }0}\)
与此同时可以发现,由于dp[i]只和dp[i-1]相关,因此,不需要进行二维的记录
class Solution:
def waysToReachTarget(self, target: int, types: List[List[int]]) -> int:
MOD = 10 ** 9 + 7
dp = [0] * (target + 1)
dp[0] = 1
for cnt,v in types:
new_dp = dp[:]
for j in range(1,1+cnt):
for i in range(target,max(-1,j*v-1),-1):
new_dp[i] += dp[i-j*v]
new_dp[i] %= MOD
dp = new_dp
return dp[-1]
此外,根据上述代码,发现对于dp[j]其总是需要查看dp[j-x],其中\(x>0\),这说明了,得到分数较大的不影响得到分数较小的dp值,因而可以进行简化操作,变成不需要第二个维度的dp
class Solution:
def waysToReachTarget(self, target: int, types: List[List[int]]) -> int:
MOD = 10 ** 9 + 7
dp = [0] * (target + 1)
dp[0] = 1
for cnt,v in types:
for i in range(target,v-1,-1):
for j in range(i-v, max(-1,i-cnt*v-1), -v):
dp[i] = (dp[i]+dp[j])%MOD
return dp[-1]

浙公网安备 33010602011771号