[DP005] 完全平方数

707 阅读2分钟

本文正在参加「Java主题月 - Java开发实战」,详情查看:juejin.cn/post/696719…

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

题目 - 完全平方数

279. 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,14916 都是完全平方数,而 311 不是。

示例 1:

输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

提示:

  • 1 <= n <= 104

方法签名

public int numSquares(int n) {

}

First try

如果要找出最小的值,那么我们可以先从整个结果集入手。

先假设我们有这么一个函数:

函数F(N),F(N)返回组成N完全平方数的 最少数量

那么

F(N+a*a) = 1 + F(N)

换一下位置和定义,就变成了:

F(N) = F(N-a*a) +1

同时我们对a进行定义

a在区间[0,squrt(N)]里

同时注意到N小于等于104,那么我们用104作为上限就可以了。

那么我们可以得出递归的方法:

 public static int getMinNum(int n){
        int max = (int)Math.sqrt(n);
        if(n<0) return 104;
        if(n == 0) return 0;
        if(n == max*max) return 1;
        int[] res = new int[max];
        for (int i = 0; i < max; i++) {
            res[i] = 1 + getMinNum(n-(i+1)*(i+1));
        }
        int min = 104;
        for (int i = 0; i < res.length; i++) {
            int re = res[i];
            min = Math.min(min, re);
        }
        return min;
    }

执行结果:

超出时间限制

最后执行的输入:

63

没关系,至少说明我们的递归公式,思路上正确了。

那么接下来我们就开始用DP数组来替代递归栈做记录。

dp数组记忆化

我们记:

dp[n]

为递归中F(n)的结果,并将递归修改为循环,同时:将对应的平方值初始化到DP数组中,除此之外不做其他的改动。

  • 因为

    dp[n] = 1 + dp[n - a*a]

    那么我们需要从左边开始(数组的左边)开始循环计算DP。

    • 如果从右边开始,可能会取不到值。
    • 基于平方数的常识,我们容易得知在初始化之后,数组的左边的记录会比右边的密集。

得出结果如下:

public static int numSquares2(int n) {
        int[] dp = new int[n+1];
        int mx = (int)Math.sqrt(n);
        for(int i = 0;i<=mx;i++){
            dp[i] = i*i;
        }

        for(int i=2;i<=n;i++){
            int min = i;
            int nx = (int)Math.sqrt(i);
            for(int j = nx;j>1;j--){
                min = Math.min(min,1+dp[i-j*j]);
            }
            dp[i] = min;
        }
        return dp[n];
    }

执行用时:34 ms, 在所有 Java 提交中击败了83.86%的用户

内存消耗:37.5 MB, 在所有 Java 提交中击败了65.62%的用户

我们可以将数组的初始化一并放到dp的计算之中,节省初始化的时间。

由于平方数并不是连续的值,因此dp数组不能使用滚动数组的方式进行优化替换,因此这道题纯DP的解法到这里就结束了。