java数据结构与算法刷题-----LeetCode279. 完全平方数

文章介绍了如何使用动态规划解决四平方和定理问题,计算1到n中每个数字所需的最少完全平方数。通过递推公式和dp数组实现,讨论了时间复杂度O(n√n)和空间复杂度O(n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.youkuaiyun.com/grd_java/article/details/123063846

在这里插入图片描述

动态规划

解题思路:时间复杂度O( n ∗ n n*\sqrt{n} nn ),空间复杂度O( n n n)
  1. DP数组及下标含义
  1. 我们要求出的是从1到n,每个数字所需的最少完全平方数。显然dp数组中存储的就是当前下标i的最少所需完全平方数。要求出谁的?显然是求出i的。那么下标就是代表求谁的,很显然,只需要一个下标,也就是一维数组。
  1. 递推公式
  1. 假设我们求出了4的最少所需完全平方数为1,也就是4本身为一个完全平方数。则dp[4]=1
  2. 此时我们要求8的最少所需完全平方数,我们可以选择的完全平方数有1和4,因为下一个完全平方数是9,9>8了,显然9往后的完全平方数都不满足条件。
  3. 显然我们不能选择1作为8需要的平方数的其中之一,因为它太小了,我们要让数量尽可能少
  4. 则我们选择4,选择一个4之后,8 - 4 = 4.也就是我们还剩下4需要找完全平方数。此时我们访问dp[4] = 1,发现4这个数字最少需要1个完全平方数。
  5. 因此8这个数,当我们第一个数选择4时,剩下的就直接访问dp[8 - 4] = dp[4] = 1即可。也就是一共需要2个完全平方数。dp[8] = 2.
  6. 综上所述,递推公式为dp[i] = min(dp[i - j 2 j^2 j2])+1, 其中j的取值范围为[1, j \sqrt{j} j ]

j 2 j^2 j2是完全平方数, i − j 2 i - j^2 ij2表示第一个数选择 j 2 j^2 j2,通过dp[ i − j 2 i - j^2 ij2]获取减去第一个数字后,剩余的数所需最少完全平方数
min(dp[i - j 2 j^2 j2])的含义就是,不断尝试不同的完全平方数作为第一个,并获取剩余数字所需最少完全平方数,找到所需数字最少的一个方案。
当剩余数字 i − j 2 i-j^2 ij2最少方案找到后,+1即可,因为我们前面减去了 j 2 j^2 j2,现在需要将 j 2 j^2 j2这个数字算上一个

  1. dp数组初始化

在这里插入图片描述

  1. 数组遍历顺序(单重循环,无需考虑遍历顺序,一共就一维,哪里来的谁先谁后)
  2. 打印dp数组(自己生成dp数组后,将dp数组输出看看,是否和自己预想的一样。)

在这里插入图片描述

代码

在这里插入图片描述

class Solution {
    public int numSquares(int n) {
        int[] f = new int[n + 1];//dp数组,代表和为i的完全平方数最少数量。下标i表示求谁的所需完全平方数最少数量
        for (int i = 1; i <= n; i++) {//1到n开始规划每个数字的完全平方数最少数量
            int minn = Integer.MAX_VALUE;
            for (int j = 1; j * j <= i; j++) {//j表示完全平方数,从1开始,1,4,9,16....,但是不能>i
                //是否选择当前完全平方数。如果选择当前完全平方数,则i - 当前完全平方数,看看剩余数字的所需完全平方数数量
                //如果选择当前完全平方数所需数量更少(更优),就令minn = f[i - j*j],否则不选择当前数。minn不变
                minn = Math.min(minn, f[i - j * j]);
            }
            f[i] = minn + 1;//当前数字所需的最少完全平方数数量。
        }
        return f[n];//返回n的最少所需完全平方数数量
    }
}

四平方和定理

  1. 四平方和定理:任意正整数x,可被最多4个正整数的平方和(完全平方数)表示。
  2. 结论一:当 n = 4 k ∗ ( 8 ∗ m + 7 ) n = 4^k * (8*m + 7) n=4k(8m+7)时,n只能被4个正整数的平方和表示。

这是一个判断条件,k和m表示随便一个数,只要n能满足即可。可以说只要 n 满足 4 k ∗ ( 8 ∗ m + 7 ) n 满足 4^k * (8*m + 7) n满足4k(8m+7)这样的条件,n就只能被4个正整数平方和表示

  1. 结论二:当前仅当 n ≠ 4 k ∗ ( 8 ∗ m + 7 ) n \not = 4^k * (8*m + 7) n=4k(8m+7)时,n才可以被3个及以下(至多3个)正整数平方和表示
解题思路:时间复杂度O( n \sqrt{n} n ),空间复杂度O( 1 1 1)
  1. n = 4 k ∗ ( 8 ∗ m + 7 ) n = 4^k * (8*m + 7) n=4k(8m+7)时,直接返回4,因为满足这个条件的正整数只能被4个完全平方数表示
  2. n ≠ 4 k ∗ ( 8 ∗ m + 7 ) n \not = 4^k * (8*m + 7) n=4k(8m+7)时,则需要额外处理,因为它表示n可能被1个或2个或3个完全平方数表示。
  1. 若当前n正好是完全平方数,则答案为1,也就是它本身表示自己
  2. 若n不是完全平方数,枚举两个完全平方数,查看是否可以组成n,也就是 n = a 2 + b 2 n = a^2 + b^2 n=a2+b2.枚举需要时间复杂度为O( n \sqrt{n} n )

因为需要枚举一个完全平方数a,a属于[1, n \sqrt{n} n ], 并判断 n − a 2 n - a^2 na2是否是完全平方数

  1. 以上两个条件都不满足,则需要3个完全平方数表示,返回3即可
代码

在这里插入图片描述

class Solution {
    // 判断是否为完全平方数,如果是完全平方数,则只需1个完全平方数(它本身)即可表示
    public boolean isPerfectSquare(int x) {
        int y = (int) Math.sqrt(x);
        return y * y == x;
    }
    // 判断是否能表示为 4^k*(8m+7)
    public boolean checkAnswer4(int x) {
        while (x % 4 == 0) x /= 4;//不断除以4,相当于不断进行4^(k-1)操作,直到k归0
        //此时剩余(8m+7),我们取余8,则剩下的数字为不可以被8取余的数
        //如果剩下的数字正好是7,这返回true,否则返回false
        return x % 8 == 7;
    }
    public int numSquares(int n) {
        if (isPerfectSquare(n)) return 1;//如果n是完全平方数,返回1
        if (checkAnswer4(n)) return 4;//满足n = 4^k * (8*m + 7),则只能由4个完全平方数表示
        //不满足n = 4^k * (8*m + 7),并且n本身不是完全平方数,则只能由2个或者3个完全平方数表示
        //先试图用2个完全平方数表示n,如果成功,返回2
        for (int i = 1; i * i <= n; i++) {
            //选择i*i作为当前完全平方数的其中之一,
            int j = n - i * i;//则减去这个完全平方数,剩下的数字为n - i * i
            if (isPerfectSquare(j)) return 2;//如果剩下的数字也是完全平方数,则可以由2个数字表示
        }
        //无法用2个完全平方数表示,返回3
        return 3;
    }

    

    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ydenergy_殷志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值