题解:洛谷 P1679 神奇的四次方数

【题目来源】

洛谷:P1679 神奇的四次方数 - 洛谷

【题目描述】

将一个整数 \(m\) 分解为 \(n\) 个四次方数的和的形式,要求 \(n\) 最小。例如,当 \(m=706\) 时,因为 \(706=5^4+3^4\),所以有 \(n=2\)。可以证明此时 \(n\) 最小。

【输入】

一行,一个整数 \(m\)

【输出】

一行,一个整数 \(n\)

【输入样例】

706

【输出样例】

2

【核心思想】

  1. 问题分析:给定整数 \(m\),需要将其分解为若干个四次方数的和,使得使用的四次方数个数最少。这是一个完全背包求最少个数问题,物品为四次方数 \(i^4\)\(i = 1, 2, \dots\)),每个物品可以使用无限次,目标是用最少的物品凑出总和 \(m\)

  2. 算法选择

    • 完全背包:每个四次方数可以使用无限次
    • 求最小值版本dp[j] 表示凑成数字 \(j\) 所需的最少四次方数个数
    • 预处理四次方数\(i^4\)\(i = 1, 2, \dots, 18\)),因为 \(18^4 = 104976 > 10^5\)
  3. 关键步骤

    • 初始化dp[0] = 0(凑出0需要0个数),其余 dp[j] = INF(无穷大)
    • 预处理物品:四次方数 \(x = i^4\)\(i\)\(1\)\(18\)(因为 \(18^4 > 10^5\)
    • 完全背包转移:对于每个四次方数 \(x\),遍历 \(j\)\(x\)\(m\)
      • dp[j] = min(dp[j], dp[j - x] + 1)
      • 要么不使用当前的 \(x\),要么使用一个 \(x\)(个数+1)
    • 答案获取dp[m] 即凑出 \(m\) 所需的最少四次方数个数
  4. 时间/空间复杂度

    • 时间复杂度:\(O(k \times m)\),其中 \(k = 18\) 为四次方数个数,\(m\) 为目标数
    • 空间复杂度:\(O(m)\),一维DP数组
  5. 完全背包求最少个数的核心思想

    • 物品无限使用:完全背包允许每个物品使用任意次数
    • 正向遍历:与01背包不同,完全背包使用正向遍历 j\(x\)\(m\)
    • 最小值转移dp[j] = min(dp[j], dp[j - x] + 1),表示使用一个 \(x\) 后的最小个数
    • 预处理优化:先计算出所有可能的四次方数,避免重复计算
    • 适用于"用最少物品凑出目标和"的完全背包问题

【算法标签】

普及- #完全背包

【代码详解】

#include<bits/stdc++.h>
using namespace std;

int n, dp[100005];  // n: 目标数,dp: 动态规划数组

int main()
{
    cin >> n;  // 读入目标数

    memset(dp, 0x3f, sizeof(dp));  // 初始化dp数组为无穷大
    dp[0] = 0;  // 凑出0需要的数字个数为0

    // 完全背包问题
    for (int i = 1; i <= 18; i++)  // 遍历所有可能的四次方数
    {
        int x = i * i * i * i;  // 计算i的四次方
        for (int j = x; j <= n; j++)  // 从x开始遍历到n
        {
            // 状态转移方程:dp[j] = min(dp[j], dp[j-x] + 1)
            // 要么不使用当前的x,要么使用当前的x
            dp[j] = min(dp[j], dp[j - x] + 1);
        }
    }

    cout << dp[n];  // 输出凑出n所需的最小数字个数
    return 0;
}

【运行结果】

706
2
posted @ 2026-06-15 09:50  团爸讲算法  阅读(8)  评论(0)    收藏  举报