LeetCode-完全平方数numSquares

题目要求

/**
 * @author yangshuo
 * @date 2020/3/18 13:36
 *
 * 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)
 * 使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
 *
 * 示例 1:
 *
 * 输入: n = 12
 * 输出: 3
 * 解释: 12 = 4 + 4 + 4.
 *
 * 示例 2:
 *
 * 输入: n = 13
 * 输出: 2
 * 解释: 13 = 4 + 9
 */

拿到题目的感觉和之前做过的钱凑整数的题目差不多,我自己能想到的方法只有两种,一种递归,一种动态规划

题解里还有两种方法:

BFS和拉格朗日四平方和定理

先说结论,执行耗时递归>DP>BFS>拉格朗日四平方和定理

递归

递归的方式比较好实现,就是把每种情况都穷举一遍,看哪种组合用到的数字最小,递归过程中可以判断组合有没有出现过,剪枝之后,耗时会下降很多,不然通不过。

动态规划

public int numSquares(int n) {
        // 完全平方数的数列
        int[] num = new int[n + 1];
        for (int i = 1; i <= n; i++){
            num[i] = i; // 初始化当前i位置的数字,代表这个数组最大由多少个1组成
            for (int j = 1; i - j * j >= 0; j++){
                // 求num[i] 最少由几个数字组成
                num[i] = Math.min(num[i],num[i - j * j]+1);
            }
        }
        return num[n];
    }

动态规划的推到公式:

假设最小公式值m=ƒ(n)
那么n的值满足下列公式 ∑(A[i] * A[i]) = n
令 k 为满足最小值 m 的时候,最大的平方数  。 令  d + k * k = n ;  d >= 0;
注意:一定要是满足m最小的时候的k值,一味的取最大平方数,就是贪心算法了
得出 f(d) + f(k*k) = f(n);
显然 f(k*k) = 1; 则  f(d) + 1 = f(n); 因为 d = n - k*k;
则可以推出ƒ(n - k * k) + 1 = ƒ(n) ;  且 k * k <= n;

看的时候没明白为什么f(k*k)=1,想了一下,这里的1表示的是个数1,不是完全平方数1,因为d=n-k*k,所以一定存在一个完全平方数加上d状态下的累加值等于n,所以这里构成n状态的数字个数就是构成n-k*k状态的数字个数加1

 

BFS

宽度优先遍历的思想,是把想要凑成的数字N,作为根节点,然后按照每个完全平方数减,按顺序入队,每一次部分数字入队算作一层,那么N-i*i减到到小于0时,说明不能通过i*i来凑成N,如果等于0,那么说明凑成N的最小数量就是节点的层数。如果大于0 就入队,作为下次遍历的数字。

public int numSquares(int n) {
        Queue<Integer> queue = new LinkedList<>();
        int[] already = new int[n + 1];
        queue.add(n);
        already[n] = 1;
        while(!queue.isEmpty()){
            int poll = queue.poll();
            for (int i = 1; ; i++){
                // 小于0表示已经不能凑成目标数字了
                int num = poll - i * i;
                if (num < 0){
                    break;
                }
                if (already[num] != 0){
                    continue;
                }
                // 由于是广度优先遍历,所以第一次能刚好减成0的就是最小组成数字
                if (num == 0){
                    return already[poll];
                }
                queue.add(num);
                already[num] = already[poll] + 1;
            }
        }
        return -1;

    }

 

拉格朗日四平方和定理

定义

  • 每个正整数均可表示为4个整数的平方和。
  • 它是费马多边形数定理和华林问题的特例。

那么可以知道只有1,2,3,4 四种情况,并且有一个推论

那么就是先判断是不是自身就是完全平方数,返回1

再判断是不是(n - 7) % 8 == 0,是的话返回4

再判断从1开始两个数字的完全平方和可以等于n么,如果有的话,返回2

都不符合就返回3

public int numSquares(int n) {
        if (Math.pow(Math.floor(Math.sqrt(n)), 2) == n) return 1;
        while ((n & 3) == 0) {
            n >>= 2;
        }
        if ((n & 7) == 7) {
            return 4;
        }
        for (int y, x = 1; x * x < n; x++) {
            y = (int) Math.floor(Math.sqrt(n - x * x));
            if (x * x + y * y == n) return 2;
        }
        return 3;
    }

微信公众号:二虎程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值