列表

详情


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

给你两个  整数 n 和 x 。

请你返回将 n 表示成一些 互不相同 正整数的 x 次幂之和的方案数。换句话说,你需要返回互不相同整数 [n1, n2, ..., nk] 的集合数目,满足 n = n1x + n2x + ... + nkx 。

由于答案可能非常大,请你将它对 109 + 7 取余后返回。

比方说,n = 160 且 x = 3 ,一个表示 n 的方法是 n = 23 + 33 + 53 

 

示例 1:

输入:n = 10, x = 2
输出:1
解释:我们可以将 n 表示为:n = 32 + 12 = 10 。
这是唯一将 10 表达成不同整数 2 次方之和的方案。

示例 2:

输入:n = 4, x = 1
输出:2
解释:我们可以将 n 按以下方案表示:
- n = 41 = 4 。
- n = 31 + 11 = 4 。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int numberOfWays(int n, int x) { } };

java 解法, 执行用时: 43 ms, 内存消耗: 41.7 MB, 提交时间: 2023-07-24 10:14:51

class Solution {
    public static final int MOD = (int)(1e9 + 7);
    public int numberOfWays(int n, int x) {
        List<Integer> nums = new ArrayList<>();// 所有可能的x的幂
        int res = 0;
        for (int i = 1; Math.pow(i, x) <= n; i++){
            nums.add((int)Math.pow(i, x));
        }
        // 01背包问题,不可重复
        int[] dp = new int[n + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++){
            for (int j = n; j >= nums.get(i); j--){
                dp[j] = (dp[j] + dp[j - nums.get(i)]) % MOD;
            }
        }
        return dp[n];
    }
}

python3 解法, 执行用时: 3960 ms, 内存消耗: 668.2 MB, 提交时间: 2023-07-24 10:14:04

class Solution:
    # 方法一:记忆化搜索
    def numberOfWays(self, n: int, x: int) -> int:
        mod = 10 ** 9 + 7
        @cache
        # 函数dfs(num, target)的解释:
        # num:当前遍历到的数
        # target:期望组合成的数
        # dfs()的意义:找到以整数num开始的,能形成幂和的方案数
        # 以例题1:"输入:n = 10, x = 2" 为例,可以由下面步骤得来:
        # 1. 从整数1,2,3中依次寻找, 从1开始
        # 2-1. 如果到达1,如果选择了1,那方案数为dfs(0, 10 - 1 ** 2),即dfs(0, 9)
        # 2-2. 如果到达1,但是没有选择1,那方案数为dfs(0, 10),即到达0,目的仍然为10的方案数
        # 3. 所以到达1的方案数dfs(1, 10) = dfs(0, 10 - 1 ** 2) + dfs(0, 10)
        def dfs(num, target):
            # 如果target为0,说明已经组合成功,则返回1(种情况)
            if target == 0:
                return 1
            # 如果当前数的x幂已经超过target了,说明后面已经没必要进行下去(因为是正整数)
            if num ** x > target:
                return 0
            # 选择了当前的整数的方案数
            res = dfs(num+1, target- num ** x) % mod
            # 加上不选择当前的整数的方案数
            res = (res + dfs(num+1, target)) % mod
            return res
        # dfs(1, n)意思是从整数1开始找,目标是n的方案数
        return dfs(1, n)

    # 方法二:二维DP/背包问题,时间O(nlogn),空间O(nlogn)
    def numberOfWays_2(self, n: int, x: int) -> int:
        mod = 10 ** 9 + 7
        # 此处用n+1的原因是防止精度丢失,比如n=64, x = 3,math.pow(64, 3) = 2.999999,会导致max_num = 2
        max_num = int(math.pow(n+1, 1/x))
        # DP[num][target]意义为:方案选到整数num时,能够组成幂和为target的方案数
        dp = [[0] * (n+1) for _ in range(max_num+1)]
        # 初始化:方案选为0时,幂和为0的方案数为1
        dp[0][0] = 1
        for num in range(1, max_num+1):
            for target in range(n+1):
                # 不选num数时
                dp[num][target] = dp[num-1][target]
                # 选num数时,需要保证target-num ** x >=0,能才选
                if target >= num ** x:
                    dp[num][target] = (dp[num][target] + dp[num-1][target-num ** x]) % mod
        return dp[max_num][n]


    # 方法三:优化后的一维DP/背包问题, 时间O(nlogn),空间O(logn)
    def numberOfWays_3(self, n: int, x: int) -> int:
        mod = 10 ** 9 + 7
        max_num = int(math.pow(n+1, 1/x))
        dp = [0] * (n+1)
        dp[0] = 1
        for num in range(1, max_num+1):
            # 此处为倒序
            # 以上述二维dp的转移方程为例:dp[num][target] = dp[num-1][target] + dp[num][target-num**x]
            # 可以知道当前的状态dp[num][target]只与上一行有关,而且只与上一行的并且列数小于等于当前列的状态有关
            # 请大家画个矩阵出来,就可以容易的看出,只有使用倒序,在更新最新一行时,不会覆盖对后面有意义的上一行的数据
            for target in range(n, -1, -1):
                if target >= num ** x:
                    dp[target] = (dp[target] + dp[target - num ** x] ) % mod
        return dp[n]

golang 解法, 执行用时: 4 ms, 内存消耗: 4.4 MB, 提交时间: 2023-07-24 10:13:05

func numberOfWays(n, x int) int {
	f := make([]int, n+1)
	f[0] = 1
	for i := 1; pow(i, x) <= n; i++ {
		v := pow(i, x)
		for s := n; s >= v; s-- {
			f[s] += f[s-v]
		}
	}
	return f[n] % (1e9 + 7)
}

func pow(x, n int) int {
	res := 1
	for ; n > 0; n /= 2 {
		if n%2 > 0 {
			res = res * x
		}
		x = x * x
	}
	return res
}

python3 解法, 执行用时: 48 ms, 内存消耗: 16 MB, 提交时间: 2023-07-24 10:12:24

'''
0-1背包问题
n看做背包容量, ni^x 看做物品,装满背包有几种物品组合方法
'''
MX_N, MX_X = 300, 5
f = [[1] + [0] * MX_N for _ in range(MX_X)] # 5行301列的二维矩阵
for x in range(MX_X):
    for i in count(1):
        v = i ** (x + 1)
        if v > MX_N: break
        for s in range(MX_N, v - 1, -1):
            f[x][s] += f[x][s - v]

class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        return f[x - 1][n] % (10 ** 9 + 7)

上一题