【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]
posted @ 2024-07-14 22:51  TICSMC  阅读(6)  评论(0)    收藏  举报