题解:洛谷 P11246 小杨和整数拆分

【题目来源】

洛谷:P11246 [GESP202409 六级] 小杨和整数拆分 - 洛谷

【题目描述】

小杨有一个正整数\(n\),小杨想将它拆分成若干完全平方数的和,同时小杨希望拆分的数量越少越好。

小杨请你编写程序计算出总和为\(n\) 的完全平方数的最少数量。

【输入】

第一行包含一个正整数\(n\),含义如题面所示。

【输出】

输出一个整数,代表总和为\(n\) 的完全平方数的最少数量。

【输入样例】

18

【输出样例】

2

【核心思想】

  1. 问题分析:给定正整数 \(n\),将其拆分为若干完全平方数之和,求最少拆分数量。这是一个完全背包动态规划问题,核心在于将问题转化为:用面值为 \(1^2, 2^2, 3^2, \ldots\) 的"硬币"凑成金额 \(n\),求最少硬币数。

  2. 算法选择

    • 一维线性 DP:设 \(dp[i]\) 表示总和为 \(i\) 的完全平方数的最少数量
    • 完全背包转移\(dp[i] = \min_{j^2 \leq i}(dp[i - j^2] + 1)\)
  3. 关键步骤

    • 初始化\(dp[0] = 0\)\(dp[i] = i\)(最坏情况用 \(i\)\(1^2\)
    • DP 计算\(i\)\(1\)\(n\)):
      • \(dp[i] \leftarrow i\)(初始化为最坏情况)
      • 枚举 \(j\)\(1\)\(\lfloor \sqrt{i} \rfloor\)
        • \(dp[i] \leftarrow \min(dp[i], dp[i - j^2] + 1)\)
    • 输出结果\(dp[n]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot \sqrt{n})\),外层 \(n\),内层 \(\sqrt{n}\)
    • 空间复杂度:\(O(n)\),一维 \(dp\) 数组
  5. 完全背包 DP 的核心思想

    • 状态定义\(dp[i]\) 表示组成和为 \(i\) 所需的最少完全平方数个数,具有最优子结构——\(dp[i]\) 可由更小的 \(dp[i - j^2]\) 推导而来
    • 完全物品特性:每个完全平方数 \(j^2\) 可以使用无限次,因此只需从小到大枚举 \(i\),每次考虑所有可能的 \(j^2 \leq i\)
    • 初始化的边界意义\(dp[i] = i\) 对应全部用 \(1^2\) 组成,这是上界保证,确保 \(\min\) 操作有合法初值
    • 数学背景:该问题与拉格朗日四平方和定理相关——任何正整数可表示为不超过 \(4\) 个完全平方数之和,因此答案范围很小(\(1\)-\(4\)),但 DP 方法通用且易于理解
    • 适用于无限选取、求最少数量的组合优化类 DP 问题

【算法标签】

普及- #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;  // 定义最大范围
int n;  // 输入的数字
int dp[N];  // dp数组

int main()
{
    cin >> n;  // 输入n

    // 动态规划计算
    for (int i = 1; i <= n; i++) {
        dp[i] = i;  // 初始值:最坏情况,全部用1^2表示
        for (int j = 1; j <= sqrt(i); j++) {  // j从1到√i
            dp[i] = min(dp[i - j*j] + 1, dp[i]);
        }
    }

    cout << dp[n];  // 输出结果
    return 0;
}

【运行结果】

18
2
posted @ 2026-07-17 14:52  团爸讲算法  阅读(8)  评论(0)    收藏  举报