首先不能使用贪心算法,以12为例,如果用贪心则为 12 = 9 + 1 + 1 + 1, 与实际情况12 = 4 + 4 + 4不符。
☆☆☆☆思路1: BFS。
一层一层的算,第一层依次减去一个平方数得到第二层,第二层依次减去一个平方数得到第三层。直到某一层出现了 0,此时的层数就是我们要找到平方数和的最小个数。
如下图,灰色表示当前层重复的节点,不需要处理。此时是第 3
层(从 0
计数),所以答案是3.
注:visited应该可以用一个数组记录(visited[n]),表示当前数字是否曾经入队,如果入过队了则不用再入队,因为这条路径长度必定大于等于之前入队的那条路径长度(在同一层是等于,在上一层是大于),图中漏标几个数字。
☆☆☆☆思路2:动态规划。与零钱兑换问题类似,见【40讲系列13】动态规划。
class Solution { public int numSquares(int n) { /** * 方法1: 动态规划 * dp[i]:表示完全平方数和为 i 的 最小个数 */ /* int[] dp = new int[n + 1]; dp[0] = 0; for (int i = 1; i <= n; i++) { dp[i] = i; // 最差的情况下,全由1相加得到 //依次减去一个平方数 for (int j = 1; i - j*j >= 0; j++) { // 注意i - j*j可以取到0 dp[i] = Math.min(dp[i], dp[i - j * j] + 1); } } return dp[n]; */ /** * 方法2: BFS */ Queue<Integer> queue = new LinkedList<>(); queue.offer(n); boolean[] visited = new boolean[n + 1]; visited[n] = true; int level = 0; 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 temp = cur - j*j; if (temp == 0) { return level; } if (!visited[temp]) { queue.offer(temp); visited[temp] = true; } } } } return 0; } }