【一看就会一写就废 指间算法】将一个数字表示成幂的和的方案数 —— 动态规划 0-1背包

38 阅读2分钟

题目:

给你两个 正 整数 n 和 x 。

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

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

比方说,n = 160 且 x = 3 ,一个表示 n 的方法是 n = 2^3 + 3^3 + 5^3 。

示例 1:

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

示例 2:

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

  • n = 4^1 = 4 。
  • n = 3^1 + 1^1 = 4 。

提示:

1 <= n <= 300
1 <= x <= 5

分析:

将n看成背包容量,将选中的数字看成价值,就变成了选择不同价值的物品将背包装满的方案数,即0-1背包问题; 动态规划一般分为3步走:

  • 确定dp数组含义: 设 dp[j] 表示使用若干个互不相同的数,其 x 次幂的和为 j 的方案数。

  • 状态转移方程: 实际计算时,可以倒序从大到小枚举 j,当尝试加入数字 i 时,此时可得到如下推论:

    • 如果满足 j<ixj<i^x 时,则 dp[j] 保持不变;

    • 如果满足 jixj≥i^x 时, dp[j]=dp[j]+dp[jix]dp[j]=dp[j]+dp[j−i^x]

  • 初始化: dp[0] = 1 这是一种常见技巧,用来触发起点计数,此处其含义就是总和为0的方案数为1。

依次计算并返回 dp[n] 即为答案。

AC代码:

class Solution {
public:
    int numberOfWays(int n, int x) {
        vector<long long> dp(n+1); 
        dp[0] = 1;

        for (int i = 1; pow(i, x) <= n; i++) {
            int v = pow(i, x);
            for (int j = n; j >= v; j--) {
                dp[j] += dp[j - v];
            }
        }

        return dp[n] % 1'000'000'007;
    }
};