文章

2787. 将一个数字表示成幂的和的方案数(Rating 1817)

2787. 将一个数字表示成幂的和的方案数(Rating 1817)

以下内容偏向于记录个人练习过程及思考,非常规题解内容

题目

2787. 将一个数字表示成幂的和的方案数

Rating 1817

思路

典型0-1背包问题

其中从1到n为n个物品,每个数字的x次方表示该物品的体积,背包总容量为n

选/不选每个物品,使用动态规划解决。

代码

二维数组写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        nums = []
        for i in range(1, n + 1): # 将可能的数字预处理出来
            if i ** x > n: # 一定是小于等于n的
                break
            nums.append(i ** x)
        MOD = 10 ** 9 + 7
        m = len(nums) # 共m个物品
        # dp[i][j] 表示前i个数字和为j的方案数,注意对于背包问题:1 <= i <= m, 0 <= j <= n
        dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
        dp[0][0] = 1 # 初始化,前0个数字和为0的方案数是1
        for i in range(1, m + 1):
            for j in range(0, n + 1):
                dp[i][j] = dp[i - 1][j] # 不选第i个物品
                if j >= nums[i - 1]:
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - nums[i - 1]]) % MOD # 选第i个物品
        return dp[m][n]

一维数组写法

由于第i行的dp[i][j]仅与第i - 1行的dp[i - 1][j]和dp[i - 1][j - nums[i - 1]]有关,即仅与上一行的当前列和较小的列有关。

因此可以优化为一维数组,从大到小倒序遍历j这一维(因为先更新较大的列不会影响更新较小的列),减少空间复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        nums = []
        for i in range(1, n + 1):
            if i ** x > n:
                break
            nums.append(i ** x)
        MOD = 10 ** 9 + 7
        m = len(nums)
        dp = [0 for _ in range(n + 1)]
        dp[0] = 1
        for i in range(1, m + 1):
            for j in range(n, nums[i - 1] - 1, -1): # 倒序。因为倒序,也可以省掉一个if
                dp[j] = (dp[j] + dp[j - nums[i - 1]]) % MOD
        return dp[n]
本文由作者按照 CC BY 4.0 进行授权