题意说明
给定正整数n,找到若干个完全平方数(比如:1,4,9,16,……)使得它们的和等于n。找出组成和的完全平方数的最小个数。‘
思路
看到最XXX,第一反应一般是动态规划或贪心。不过这一题贪心不能采取不断选取范围内的最大完全平方数的策略。比如:12 = 4 + 4 + 4为最优解,但是若采取如上所示的贪心策略,则:12 = 9 + 1 + 1 + 1并不是最优解。
此题也可以采取广度优先搜索BFS的方式进行求解,大致思路如下:
- 设置ans = 0,max = Math.sqrt(n)来限制完全平方数的范围,queue用于进行BFS操作;
- 向queue中加入0,表示初始状态;
- 按照一般BFS的步骤来,每次遍历处理当前queue中所有的数字,弹出queue中的数字temp,将其加上一个完全平方数x,此时有如下几种情况:
(1) temp + x < n :queue.add(temp + x);
(2) temp + x == n :找到最佳答案返回ans,结束BFS过程;
(3) temp + x > n:此时超出了n的范围,不需要继续取更大的完全平方数,因为此情况下怎样都不可能满足等于n的条件。
参考代码:
class Solution {
public int numSquares(int n) {
int max = (int)Math.sqrt(n),ans = 1;
if(max * max == n) return ans;
Queue<Integer> queue = new LinkedList<>();
for(int i = 1;i <= n;i++) {
queue.add(i * i);
}
boolean flag = false;
while(!queue.isEmpty()) {
ans++;
int len = queue.size();
for(int i = 0;i < len;i++) {
int temp = queue.remove();
for(int j = 1;j <= n;j++) {
if(temp + j * j < n) {
queue.add(temp + j * j);
}
else if(temp + j * j == n) {
flag = true;break;
}
else break;
}
if(flag) break;
}
if(flag) break;
}
return ans;
}
}
此代码提交结果超时,部分用例无法通过。
说明所得结果基本正确,但是需要对时间复杂度进行小小的优化。
记忆化搜索优化
在进行BFS的过程中,可以发现queue中多次出现重复的数字。比如,n = 12时:
第一轮:queue = [ 1, 4, 9 ]
第二轮:queue = [ 2, 5, 10, 5, 8, 10]
第三轮:queue = [ 3, 6, 11, 6, 9, 11, 6, 9, 9, 12 ] (此处找到12,结束BFS过程)
因为值相同的数字在BFS过程中产生的“新数字”完全一致,对重复数字的处理是一种无谓的开销。可以采取以空间换时间的策略,使用记忆化搜索优化BFS过程。
方法:创建一个HashSet存储至今遍历过的所有数字,若temp已在set中,直接跳过不做处理。
优化代码
class Solution {
public int numSquares(int n) {
int max = (int)Math.sqrt(n),ans = 0;
Queue<Integer> queue = new LinkedList<>();
Set<Integer> set = new HashSet<>();
queue.add(0);set.add(0);
while(!queue.isEmpty()) {
ans++; // 每进行一层BFS就多一个完全平方数
/* BFS一般过程 */
int len = queue.size();
for(int i = 0;i < len;i++) {
int temp = queue.remove();
for(int j = 1;j <= max;j++) {
if(set.contains(temp + j * j)) continue; // set中已存在temp,直接跳过
else if(!set.contains(temp + j * j) && temp + j * j < n) { // temp尚未出现过且满足要求
set.add(temp + j * j);
queue.add(temp + j * j);
}
else if(temp + j * j == n) { // 找到最优解
return ans;
}
else break; // temp + j * j > n
}
}
}
return ans;
}
}
运行结果:
还是有点慢,可以尝试继续优化或者换动态规划的思路来解这一题。