魔法甜点之和:小包的新挑战| 豆包MarsCode AI刷题

57 阅读5分钟

今天我刷了这题:

image.png 我写的代码如下:



def solution(n, m, s, like):
    def factorial(n):
        ji = 1
        for i in range(1,n+1):
            ji*= i
        return ji
    n = len(like)
    total = 0
    
    # def dfs(i,target,change):
    #     nonlocal total
    #     if change > m:
    #         return
        
    #     if i == n:
    #         if target == s and change <= m:
    #             total += 1 

    #         return 
    #     dfs(i+1,target+factorial(like[i]),change + 1)
    #     dfs(i+1,target+like[i],change)
    #     dfs(i+1,target,change)
    # dfs(0,0,0)
    #dp[i][j][k] 前i-1个数字,使用j个魔法,和为k的方案数
    dp = [[[0 for _ in range(s+1)] for _ in range(m+1)] for  _ in range(n + 1)]
    for i in range(m+1):
        dp[0][0][0] = 1

    for i in range(n):
        for j in range(m+1):
            for k in range(s+1):
                if k + factorial(like[i]) <= s and k + like[i] <= s and j+1 <= m :
                    dp[i+1][j+1][k+factorial(like[i])] +=  dp[i][j][k ] 
                    dp[i+1][j][k + like[i]] += dp[i][j][k]
                
                    dp[i+1][j][k] += dp[i][j][k]
                
                elif k + factorial(like[i]) <= s and j+1 <= m:
                    # dp[i+1][j+1][k] += dp[i][j+1][k] + dp[i][j][k + factorial(like[i])]
                    dp[i+1][j+1][k+factorial(like[i])] += dp[i][j][k]
                    dp[i+1][j][k] += dp[i][j][k]

                    
                elif k + like[i] <= s :
                    # dp[i+1][j+1][k] += dp[i][j+1][k] + dp[i][j+1][k + like[i]]
                    dp[i+1][j][k+like[i]] += dp[i][j][k]
                    dp[i+1][j][k] += dp[i][j][k]
                else:
                    dp[i+1][j][k] += dp[i][j][k]
     
               
               
                

  
    for i in dp[-1]:
        total += i[s]
                
    return total
if __name__ == "__main__":
    #  You can add more test cases here j = 2 k = 3 i+1 = 2 i+1 = 3 j = 2 k = 6
    print(solution(3, 2, 6, [1,2,3]) == 5 ) 
    print(solution(3, 1, 1, [1,1,1]) == 6 )```
    
    
    
    
    
    
    
    我的代码是一个动态规划(DP)算法的实现,用来解决一个包含魔法使用的数值组合问题。具体来说,这段代码的目的是计算在使用 `m` 次魔法的限制下,如何从数组 `like` 中选择一些元素使得它们的和为 `s`,并且每个元素有两种选择:原值或其阶乘值(阶乘指的是该数值的乘积,如 3 的阶乘是 3 * 2 * 1 = 6)。任务是计算有多少种不同的选择方式。

### 主要的代码结构和步骤:

1.  **`factorial` 函数**: 这是一个计算整数阶乘的简单函数。给定一个整数 `n`,它会返回 `n!`,即 `n` 的阶乘。

1.  **动态规划数组 `dp`**: `dp[i][j][k]` 表示考虑前 `i` 个数(`like` 数组的前 `i` 个元素),使用 `j` 次魔法,且当前的和为 `k` 的方案数。

    -   `i` 是当前考虑的数字的索引。
    -   `j` 是已经使用的魔法次数。
    -   `k` 是当前和。

    `dp` 是一个三维数组,大小为 `(n+1) x (m+1) x (s+1)`,`n` 是数组 `like` 的长度,`m` 是最大允许使用的魔法次数,`s` 是目标和。

1.  **初始化**: 初始时,`dp[0][0][0] = 1` 表示在没有选择任何数字、没有使用任何魔法且和为 0 的情况下,方案数为 1。

1.  **填充动态规划表**: 使用三重循环遍历 `dp` 数组:

    -   外层循环遍历每一个数字 `i`。
    -   中层循环遍历每个魔法次数 `j`。
    -   内层循环遍历每个可能的和 `k`。

    对于每个状态 `dp[i][j][k]`,有几种选择:

    -   **不使用魔法**:当前数字的原值 `like[i]` 加到当前和 `k`,如果结果不超过目标和 `s`,则把当前方案数累加到 `dp[i+1][j][k + like[i]]`。
    -   **使用魔法**:当前数字的阶乘值 `factorial(like[i])` 加到当前和 `k`,如果结果不超过目标和 `s`,且还未超过最大魔法次数 `m`,则把当前方案数累加到 `dp[i+1][j+1][k + factorial(like[i])]`。

1.  **计算结果**: 在填充完动态规划表之后,最终的答案存在 `dp[n][j][s]` 中,其中 `j` 从 0 到 `m` 都需要考虑,因为我们不限制使用魔法的次数,只限制最大次数为 `m`。所以最后遍历 `dp[n]` 中所有的方案数,累加它们的和,得到符合条件的总方案数。

### 代码分析:

1.  **`dp[i][j][k]` 的状态转移**:

    -   如果 `k + factorial(like[i]) <= s` 且 `j + 1 <= m`,则使用阶乘值加入到和中。
    -   如果 `k + like[i] <= s`,则使用原值加入到和中。
    -   总结来说,动态规划的状态转移考虑了是否使用阶乘值或原值,并根据当前魔法次数和当前和是否满足约束来进行更新。

1.  **`factorial(like[i])` 的使用**: 对于每一个数字 `like[i]`,我们有两种选择:直接使用 `like[i]` 或使用它的阶乘值 `factorial(like[i])`。因此,算法需要计算两者对和的影响。

1.  **边界条件**: `dp[0][0][0] = 1` 是基准情况,表示在没有任何选择时,和为 0 的方式只有一种,即什么都不选。

1.  **输出**: 最后,通过遍历 `dp[n]` 数组中所有可能的魔法次数 `j`,以及和为 `s` 的方案,累加得到所有符合条件的方案数。

### 示例:

  以 `solution(3, 2, 6, [1, 2, 3])` 为例:

-   `n = 3`,意味着我们有 3 个数字 `[1, 2, 3]`。
-   `m = 2`,意味着最多可以使用 2 次魔法。
-   `s = 6`,我们希望最终的和是 6。

### 计算过程:

  在动态规划的过程中,系统会计算每一种选择的结果,最终返回的结果是总共有多少种选择方式可以使和为 `6`,并且使用的魔法次数不超过 2。

### 总结:

  这段代码实现了一个典型的动态规划算法,旨在解决带有约束条件的数值组合问题。通过合理地使用三维 `dp` 数组,程序可以高效地计算出所有符合条件的选择方案数。