题目:
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的最少数量 。
完全平方数是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
题解:
说法一:
转态转移方程的得出 f(i)=f(i-j*j)+1 假设i为组成平方数的最少个数,最后一个数值为 j*j, 由于最优子结构(无法再进行拆分,可以想象成5=1+2*2 ) 得出 f(i)=f(i-j*j)+1
什么叫最优子结构?
“知乎用户:我的理解是要求的目标函数一般都具有“单调性”,诸如绝对值,模值这种的目标函数就不具有最优子结构。”
动态规划的最优子结构问题,有什么样的问题它不满足最优子结构? - 王赟 Maigo的回答 - 知乎 https://www.zhihu.com/question/52165201/answer/288025858
关于最优子结构、无后效性和重复子问题的定义: -CSDN的回答:https://blog.csdn.net/qq_25800311/article/details/90635979
知乎回答:“我的理解是要求的目标函数一般都具有“单调性”,诸如绝对值,模值这种的目标函数就不具有最优子结构。”
说法二:
题目要求将一个给定整数拆分成若干完全平方的和,要求用到的这些完全平方数最少。
拿到题目后第一个思路可以很快想到,就是尝试通过贪心的策略解题,每次找能匹配上的最大完全平方。
比如对于例子 n = 12 我先找 3*3 = 9 然后再找 3个1。
发现对于给定的示例就是不符合的,所以这个思路作罢。
第二个思路可以想到通过广度优先遍历的策略进行解题,就是每次拿一遍小于n的完全平方数,去凑n,然后一点一点去逼近n,
先凑成n时所需的个数就是最少个数。这个方法是可行的
第三个思路就是动态规划的想法,产生这个想法的原因如下:
如果我要凑一个数 n , 假设我选择的最大完全平方为m 那么就有 m + diff = n, 要想使凑n所用到的完全平方个数最少,
下一步就转化为求 凑diff所用到的完全平方数最少,然后依次递归下去。这就是同时具备重叠子问题与最优子结构。
那么显然我们可以用动态规划的思路去做,上面描述的过程可以很简单的转化为如下的式子:
dp[n] = 1 + dp[n - m*m]; 其中m是满足n用到完全平方个数最少时,所用的某一个完全平方的根。
分析到这里我们下一步就要去求这个m,这里我们可以通过一个循环的方式,去找最小的dp[n - m*m]即可
代码如下👇👇👇,时间复杂度为O(N^(1.5));
import java.util.*;
class Solution {
public int numSquares(int n) {
// 默认初始化值都为0
int[] dp = new int[n + 1];
for (int i = 1; i <= n; i++) {
// 最坏的情况就是每次+1
dp[i] = i;
for (int j = 1; i - j * j >= 0; j++) {
// 动态转移方程
// 假如i=12,就是求问题j=2时,dp[i-j*j]最小,还是j=3时,dp[i-j*j]最小。
// 1就代表2*2或者3*3已经确定了,必须有这一个最大值,然后求dp[diff=i-j*j]的最小值。
// 重叠子问题是求min(dp[j])和min(dp[i-j*j]),它俩是一个问题
// 最优子结构是每一个问题都是dp[i-j*j]+1,“+1”代表有一个值j*j已经确定了,求其余的值的最小值。
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
}
/**
* @author wyl
*/
public class Main {
public static void main(String[] args) {
Solution s = new Solution();
int n = 12;
int res = s.numSquares(n);
System.out.println("输出结果:" + res);
}
}