题目
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
1
2
3
|
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
|
示例 2:
1
2
3
|
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
|
题解
看到这题就感觉跟找零钱那道题非常像,有一点区别是找零钱中直接限定了有哪些零钱,而这道题则是要找平方数,需要自己开方计算出范围。自己的实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
int numSquares(int n) {
int count = sqrt(n);
vector<int> dp(n + 1, n + 1);
dp[0] = 0;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= count; j++) {
int value = j * j;
if(value > i) break;
dp[i] = min(dp[i], dp[i - value] + 1);
}
}
return dp[n];
}
|
整体实现跟找零钱完全一样,这里就不再详细说明了。实现是自底向上的动态规划写法,当然网上还有采用递归的深度优先遍历或是广度优先遍历。这两种方式均是自顶向下的解法,这里列一下别人的广度优先的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public int numSquares(int n) {
Queue<Integer> queue = new LinkedList<>();
HashSet<Integer> visited = new HashSet<>();
int level = 0;
queue.add(n);
while (!queue.isEmpty()) {
int size = queue.size();
level++; // 开始生成下一层
for (int i = 0; i < size; i++) {
int cur = queue.poll();
//依次减 1, 4, 9... 生成下一层的节点
for (int j = 1; j * j <= cur; j++) {
int next = cur - j * j;
if (next == 0) {
return level;
}
if (!visited.contains(next)) {
queue.offer(next);
visited.add(next);
}
}
}
}
return -1;
}
|
广度优先需要利用到队列,通过队列一层层的计算,直到找到为0的节点,那么这个时候的层数便是最少的个数。广度优先与深度优先均会存在重复值的问题,因此要使用hash表将数据存储下来,避免重复计算,降低复杂度。