题目描述:
标签:广度优先搜索 数学 动态规划
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
代码:
《方法一:广度优先搜索》
思路分析:
将从1~n的每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。求解最小的平方数数量,就是求解从节点n到节点0的最短路径。
1、定义一个方法,保存1~n之间的所有完全平方数。完全平方数1、4、9、16...可以看出两数之差为3,5,7,...,所以它们的差值也是成等差数列增长的。
2、定义一个队列,用于保存每层的节点;定义一个boolean数组v,保存节点是否被访问过。开始时加入n节点,并修改v[n]=true。
3、对队列中的每层元素遍历,求出n的下一层节点(即与n之间相差完全平方数的节点),一直到减去一个平方数等于0为止,说明找到了最短路径。
class Solution {
public int numSquares(int n) {
List<Integer> squres = generateSquares(n);
Queue<Integer> q = new LinkedList<>();
boolean[] v = new boolean[n+1];
q.add(n);
v[n] = true;
int level = 0;
while(!q.isEmpty()){
int size = q.size();
level++;
while(size-- > 0){
int cur = q.poll();
for(int s : squres){
int next = cur - s;
if(next < 0){
break;
}
if(next == 0){
return level;
}
if(v[next]){
continue;
}
v[next] = true;
q.add(next);
}
}
}
return n;
}
public List<Integer> generateSquares(int n){
List<Integer> squres = new ArrayList<>();
int squre = 1;
int diff = 3;
while(squre <= n){
squres.add(squre);
squre += diff;
diff += 2;
}
return squres;
}
}
《二、动态规划》
思路分析:
1、确定dp数组以及下标的含义——这里dp[i]表示和为 i 的完全平方数的最少数量
2、确定递推公式,先找到从1~n中存在的所有完全平方数,在对完全平方数遍历,当当前完全平方数square>i时,退出遍历,否则需要更新最少数量,min = min(min, dp[i-square] +1),dp[i] = min
3、dp数组初始化,可以不初始化
4、确定遍历顺序,for循环遍历,i从1到n
5、举例推导dp数组
6、最后返回所有dp[i]的和
class Solution {
public int numSquares(int n) {
List<Integer> squares = generateSquares(n);
int[] dp = new int[n+1];
for(int i = 1;i <= n;i++){
int min = Integer.MAX_VALUE;
for(int square : squares){
if(square > i){
break;
}
min = Math.min(min, dp[i - square] + 1);
}
dp[i] = min;
}
return dp[n];
}
public List<Integer> generateSquares(int n){
List<Integer> list = new ArrayList<>();
int square = 1;
int diff = 3;
while(square <= n){
list.add(square);
square += diff;
diff += 2;
}
return list;
}
}