快速求解完全平方数的最少数量

LeetCode279

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

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

示例 1:

输入: n =12,输出: 3

解释: 12 = 4 + 4 + 4

示例 2:

输入: n =13,输出: 2

解释: 13 = 4 + 9

Python解法

1.BFS

python 复制代码
from collections import deque
class Solution:
    def numSquares(self, n: int) -> int:
        # 记录已经处理过的数字,避免重复入队,减少计算
        visited = set()
        # 队列元素:(当前剩余数值, 当前已用平方数个数)
        q = deque()
        # 初始起点:数字n,使用0个平方数
        q.append((n, 0))
        visited.add(n)
        
        # 队列不为空就持续遍历每一层
        while q:
            # 队首出队,处理当前层节点
            cur, step = q.popleft()
            j = 1
            # 枚举所有不大于cur的平方数 j²
            while j*j <= cur:
                next_val = cur - j*j
                # 刚好减到0,找到最少数量:当前step+本次用的1个平方数
                if next_val == 0:
                    return step + 1
                # 没访问过才入队,防止重复循环
                if next_val not in visited:
                    visited.add(next_val)
                    q.append((next_val, step + 1))
                j += 1
        return -1

2.数学方法

python 复制代码
import math
class Solution:
    def numSquares(self, n: int) -> int:
        # 判断完全平方数
        def is_square(x):
            y = int(math.sqrt(x))
            return y * y == x
        
        # 判断是否为4^k*(8m+7)
        def check_four(x):
            while x % 4 == 0:
                x //= 4
            return x % 8 == 7
        
        if is_square(n):
            return 1
        if check_four(n):
            return 4
        i = 1
        while i * i <= n:
            rest = n - i*i
            if is_square(rest):
                return 2
            i += 1
        return 3

解释(数学)

1. 拉格朗日四平方和定理

任意正整数,一定能写成最多 4 个完全平方数之和 抖音百科。 也就是说,最坏情况只用 4 个,绝对不需要 5 个及以上,答案上限就是 4。

2. 勒让德三平方和定理(关键推论)

一个数不能只用 1、2、3 个平方数凑出来,当且仅当它满足形式:

\(n=4^k \times (8m+7)\) 这种数必须恰好 4 个平方数相加,没有更少方案,直接返回 4。

Java解法

1.BFS

java 复制代码
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int numSquares(int n) {
        HashSet<Integer> visited = new HashSet<>();
        Queue<int[]> q = new LinkedList<>();
        q.add(new int[]{n, 0});
        visited.add(n);

        while (!q.isEmpty()) {
            int[] curPair = q.poll();
            int cur = curPair[0];
            int step = curPair[1];

            int j = 1;
            while ((long) j * j <= cur) {
                int nextVal = cur - j * j;
                if (nextVal == 0) {
                    return step + 1;
                }
                if (!visited.contains(nextVal)) {
                    visited.add(nextVal);
                    q.add(new int[]{nextVal, step + 1});
                }
                j++;
            }
        }
        return -1;
    }
}

2.数学方法

java 复制代码
class Solution {
    public int numSquares(int n) {
        if (isSquare(n)) return 1;
        if (checkFour(n)) return 4;
        for (int i = 1; i * i <= n; i++) {
            int rest = n - i * i;
            if (isSquare(rest)) return 2;
        }
        return 3;
    }

    // 判断完全平方数
    private boolean isSquare(int x) {
        int y = (int) Math.sqrt(x);
        return y * y == x;
    }

    // 判断 n = 4^k*(8m+7)
    private boolean checkFour(int x) {
        while (x % 4 == 0) x /= 4;
        return x % 8 == 7;
    }
}

C++解法

1.BFS

cpp 复制代码
#include <queue>
#include <unordered_set>
using namespace std;

class Solution {
public:
    int numSquares(int n) {
        unordered_set<int> visited;
        queue<pair<int, int>> q;
        q.push({n, 0});
        visited.insert(n);

        while (!q.empty()) {
            auto [cur, step] = q.front();
            q.pop();

            int j = 1;
            while (1LL * j * j <= cur) {
                int nextVal = cur - j * j;
                if (nextVal == 0) {
                    return step + 1;
                }
                if (!visited.count(nextVal)) {
                    visited.insert(nextVal);
                    q.push({nextVal, step + 1});
                }
                j++;
            }
        }
        return -1;
    }
};

2.数学方法

cpp 复制代码
#include <cmath>
using namespace std;

class Solution {
public:
    int numSquares(int n) {
        if (isSquare(n)) return 1;
        if (checkFour(n)) return 4;
        for (int i = 1; 1LL * i * i <= n; ++i) {
            int rest = n - i * i;
            if (isSquare(rest)) return 2;
        }
        return 3;
    }

private:
    bool isSquare(int x) {
        int y = sqrt(x);
        return y * y == x;
    }

    bool checkFour(int x) {
        while (x % 4 == 0) x /= 4;
        return x % 8 == 7;
    }
};
相关推荐
吃好睡好便好3 小时前
MATLAB中图像的线性变换
开发语言·图像处理·学习·计算机视觉·matlab
charlie1145141913 小时前
Cinux —— 给物理内存建账本:bitmap 物理内存管理器
开发语言·c++·操作系统·开源项目
YUS云生3 小时前
大模型学习·第41天:LangChain进阶——提示词模板与Chain链式调用
学习·langchain·c#
看昭奚恤哭3 小时前
ontainer App】Container App无法从Container Registries 拉取镜像 - 报错 Forbidden
后端·python·flask
0566463 小时前
Python康复训练——数据结构
数据结构·windows·python
ShiXZ2133 小时前
Java 8 Stream API 实用技巧详解:从入门到精通
java·开发语言
冻柠檬飞冰走茶3 小时前
PTA基础编程题目集 7-27 冒泡法排序(C语言实现)
c语言·开发语言·数据结构·算法
听雨入夜3 小时前
“同声传译”还是“全文翻译”?为何HotSpot虚拟机仍要保留解释器?
开发语言·python
随风M记忆s3 小时前
GEE&Python-demo:利用Sentinel-监测北京奥林匹克森林公园年NDVI变化(附Python版)
开发语言·python·sentinel
C++、Java和Python的菜鸟4 小时前
第9章 后端Web进阶(AOP)
java·开发语言·前端