跳至主要內容

2585. 获得分数的方法数

ITCharge大约 3 分钟

2585. 获得分数的方法数open in new window

  • 标签:数组、动态规划
  • 难度:困难

题目链接

题目大意

描述:考试中有 nn 种类型的题目。给定一个整数 targettarget 和一个下标从 00 开始的二维整数数组 typestypes,其中 types[i]=[counti,marksi]types[i] = [count_i, marks_i] 表示第 ii 种类型的题目有 counticount_i 道,每道题目对应 marksimarks_i 分。

要求:返回你在考试中恰好得到 targettarget 分的方法数。由于答案可能很大,结果需要对 109+710^9 + 7 取余。

说明

  • 同类型题目无法区分。比如说,如果有 33 道同类型题目,那么解答第 11 和第 22 道题目与解答第 11 和第 33 道题目或者第 22 和第 33 道题目是相同的。
  • 1target10001 \le target \le 1000
  • n==types.lengthn == types.length
  • 1n501 \le n \le 50
  • types[i].length==2types[i].length == 2
  • 1counti,marksi501 \le counti, marksi \le 50

示例

  • 示例 1:
输入:target = 6, types = [[6,1],[3,2],[2,3]]
输出:7
解释:要获得 6 分,你可以选择以下七种方法之一:
- 解决 6 道第 0 种类型的题目:1 + 1 + 1 + 1 + 1 + 1 = 6
- 解决 4 道第 0 种类型的题目和 1 道第 1 种类型的题目:1 + 1 + 1 + 1 + 2 = 6
- 解决 2 道第 0 种类型的题目和 2 道第 1 种类型的题目:1 + 1 + 2 + 2 = 6
- 解决 3 道第 0 种类型的题目和 1 道第 2 种类型的题目:1 + 1 + 1 + 3 = 6
- 解决 1 道第 0 种类型的题目、1 道第 1 种类型的题目和 1 道第 2 种类型的题目:1 + 2 + 3 = 6
- 解决 3 道第 1 种类型的题目:2 + 2 + 2 = 6
- 解决 2 道第 2 种类型的题目:3 + 3 = 6
  • 示例 2:
输入:target = 5, types = [[50,1],[50,2],[50,5]]
输出:4
解释:要获得 5 分,你可以选择以下四种方法之一:
- 解决 5 道第 0 种类型的题目:1 + 1 + 1 + 1 + 1 = 5
- 解决 3 道第 0 种类型的题目和 1 道第 1 种类型的题目:1 + 1 + 1 + 2 = 5
- 解决 1 道第 0 种类型的题目和 2 道第 1 种类型的题目:1 + 2 + 2 = 5
- 解决 1 道第 2 种类型的题目:5

解题思路

思路 1:动态规划

1. 划分阶段

按照进行阶段划分。

2. 定义状态

定义状态 dp[i][w]dp[i][w] 表示为:前 ii 种题目恰好组成 ww 分的方案数。

3. 状态转移方程

ii 种题目恰好组成 ww 分的方案数,等于前 i1i - 1 种问题恰好组成 wk×marksiw - k \times marks_i 分的方案数总和,即状态转移方程为:dp[i][w]=k=0dp[i1][wk×marksi]dp[i][w] = \sum_{k = 0} dp[i - 1][w - k \times marks_i]

4. 初始条件
  • 00 种题目恰好组成 00 分的方案数为 11
5. 最终结果

根据我们之前定义的状态, dp[i][w]dp[i][w] 表示为:前 ii 种题目恰好组成 ww 分的方案数。 所以最终结果为 dp[size][target]dp[size][target]

思路 1:代码

class Solution:    
    def waysToReachTarget(self, target: int, types: List[List[int]]) -> int:
        size = len(types)
        group_count = [types[i][0] for i in range(len(types))]
        weight = [[(types[i][1] * k) for k in range(types[i][0] + 1)] for i in range(len(types))]
        mod = 1000000007
            
        dp = [[0 for _ in range(target + 1)] for _ in range(size + 1)]
        dp[0][0] = 1
        
        # 枚举前 i 组物品
        for i in range(1, size + 1):
            # 枚举背包装载重量
            for w in range(target + 1):
                # 枚举第 i 组物品能取个数
                dp[i][w] = dp[i - 1][w]
                for k in range(1, group_count[i - 1] + 1):
                    if w >= weight[i - 1][k]:
                        dp[i][w] += dp[i - 1][w - weight[i - 1][k]]
                        dp[i][w] %= mod
        
        return dp[size][target]

思路 1:复杂度分析

  • 时间复杂度O(n×target×m)O(n \times target \times m),其中 nn 为题目种类数,targettarget 为目标分数,mm 为每种题目的最大分数。
  • 空间复杂度O(n×target)O(n \times target)