题目:
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
class Solution {
/*
将本题翻译为 完全背包问题:
完全平方数 就是物品,可以无条件使用
凑个整数 n 就是背包的最大容量为 n
问凑满这个背包 最少 有多少个物品。
i * i 表示第 i 个物品的重量 为 i*i
也就是 object[i] = i*i
求最少的问题,与322简直一模一样呐
动规五部曲:
1、确定 dp 数组以及下标的含义
dp[j] 和为 j 的完全平方数的最少数量为 dp[j]
2、确定递推公式
dp[j] = min(dp[j], dp[j - i * i])
3、初始化 dp 数组
dp[0] = 0
dp[j] : j 为其他数时,dp[j] = INT_MAX
4、确定遍历顺序
先物品或者先背包都可以,正序
这个题可以归约为零钱兑换,如果对 i*i 那部分有疑惑,可以先对输入 k 进行预处理,
将 k 转为 完全平方数的列表,然后这个题就完全与零钱兑换一模一样了,但是预处理
的效率会由于多一次for循环,相对直接在原地运算较低,但是是最容易想到的思路,
而且复杂度来说是一样的,可以对 ac 后的代码再进行优化,去掉预处理的 for 循环
*/
public:
int numSquares(int n) {
// dp[j] 意义:和为 j 的完全平方数的最少数量为 dp[j]
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
// // 方法一:先遍历物品,再遍历背包容量
// // 注意这里的 物品重量是 i*i 因此循环终止条件要是 i*i <= n
// for(int i = 1; i * i <= n; i++)
// {
// // 这里的 j 为什么从 1 开始,是因为本题中的 object[i] = i
// for(int j = 1; j <= n; j++)
// {
// // 除了防止数据溢出的判断,
// // 还需加一个条件,就是判断 j - i * i >= 0
// // 为了保证 背包容量 大于 物品重量
// if(j - i * i >= 0 && dp[j - i * i] != INT_MAX)
// {
// dp[j] = min(dp[j], 1 + dp[j - i * i]);
// }
// }
// }
// 方法二:先遍历背包重量,在遍历物品
for(int j = 0; j <= n; j++)
{
// for(int i = 1; i * i <= n; i++)
// {
// // 注意这个 if 可以与 for 循环中的终止条件合并一下。
// if(j >= i * i)
// {
// // 刚刚把 dp[j - i * i] 错写成了 dp(j - i * i)
// dp[j] = min(dp[j], 1 + dp[j - i * i]);
// }
// }
// 这里也可以写为:
for(int i = 1; i * i <= j; i++)
{
dp[j] = min(dp[j], 1 + dp[j - i * i]);
}
}
return dp[n];
}
};