Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

Example 1:

Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.

又是超哥一个人辛苦的更新题目,一个人托起 LeetCode 免费题的一片天空啊,赞一个~ 这道题说是给我们一个正整数,求它最少能由几个完全平方数组成。这道题是考察四平方和定理,to be honest, 这是我第一次听说这个定理,天啦撸,我的数学是语文老师教的么?! 闲话不多扯,回来做题。先来看第一种很高效的方法,根据四平方和定理,任意一个正整数均可表示为4个整数的平方和,其实是可以表示为4个以内的平方数之和,那么就是说返回结果只有 1,2,3 或4其中的一个,首先我们将数字化简一下,由于一个数如果含有因子4,那么我们可以把4都去掉,并不影响结果,比如2和8,3和12等等,返回的结果都相同,读者可自行举更多的栗子。还有一个可以化简的地方就是,如果一个数除以8余7的话,那么肯定是由4个完全平方数组成,这里就不证明了,因为我也不会证明,读者可自行举例验证。那么做完两步后,一个很大的数有可能就会变得很小了,大大减少了运算时间,下面我们就来尝试的将其拆为两个平方数之和,如果拆成功了那么就会返回1或2,因为其中一个平方数可能为0. (注:由于输入的n是正整数,所以不存在两个平方数均为0的情况)。注意下面的 !!a + !!b 这个表达式,可能很多人不太理解这个的意思,其实很简单,感叹号!表示逻辑取反,那么一个正整数逻辑取反为0,再取反为1,所以用两个感叹号!!的作用就是看a和b是否为正整数,都为正整数的话返回2,只有一个是正整数的话返回1,参见代码如下:

解法一:

class Solution {
public:
int numSquares(int n) {
while (n % == ) n /= ;
if (n % == ) return ;
for (int a = ; a * a <= n; ++a) {
int b = sqrt(n - a * a);
if (a * a + b * b == n) {
return !!a + !!b;
}
}
return ;
}
};

这道题远不止这一种解法,我们还可以用动态规划 Dynamic Programming 来做,我们建立一个长度为 n+1 的一维dp数组,将第一个值初始化为0,其余值都初始化为 INT_MAX, i从0循环到n,j从1循环到 i+j*j <= n 的位置,然后每次更新 dp[i+j*j] 的值,动态更新 dp 数组,其中 dp[i] 表示正整数i能少能由多个完全平方数组成,那么我们求n,就是返回 dp[n] 即可,也就是 dp 数组的最后一个数字。需要注意的是这里的写法,i必须从0开始,j必须从1开始,因为我们的初衷是想用 dp[i] 来更新 dp[i + j * j],如果 i=0, j=1 了,那么 dp[i] 和 dp[i + j * j] 就相等了,怎么能用本身 dp 值加1来更新自身呢,参见代码如下:

解法二:

class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + , INT_MAX);
dp[] = ;
for (int i = ; i <= n; ++i) {
for (int j = ; i + j * j <= n; ++j) {
dp[i + j * j] = min(dp[i + j * j], dp[i] + );
}
}
return dp.back();
}
};

下面再来看一种 DP 解法,这种解法跟上面有些不同,上面那种解法是初始化了整个长度为 n+1 的 dp 数字,但是初始化的顺序不定的,而这个种方法只初始化了第一个值为0,那么在循环里计算,每次增加一个 dp 数组的长度,里面那个 for 循环一次循环结束就算好下一个数由几个完全平方数组成,直到增加到第 n+1 个,返回即可,想更直观的看这两种DP方法的区别,建议每次循环后都打印出 dp 数字的值来观察其更新的顺序,参见代码如下:

解法三:

class Solution {
public:
int numSquares(int n) {
vector<int> dp(, );
while (dp.size() <= n) {
int m = dp.size(), val = INT_MAX;
for (int i = ; i * i <= m; ++i) {
val = min(val, dp[m - i * i] + );
}
dp.push_back(val);
}
return dp.back();
}
};

最后我们来介绍一种递归 Recursion 的解法,这种方法的好处是写法简洁,但是运算效率不敢恭维。我们的目的是遍历所有比n小的完全平方数,然后对n与完全平方数的差值递归调用函数,目的是不断更新最终结果,知道找到最小的那个,参见代码如下:

解法四:

class Solution {
public:
int numSquares(int n) {
int res = n, num = ;
while (num * num <= n) {
int a = n / (num * num), b = n % (num * num);
res = min(res, a + numSquares(b));
++num;
}
return res;
}
};

讨论:解法二三四的运算效率真的不高,强推解法一,高效又易懂,如果想强行优化后三个算法,可以将解法一的前两个 if 判断加到后三个的算法的开头,能很大的提高运算效率。

类似题目:

Count Primes

Ugly Number II

参考资料:

https://leetcode.com/problems/perfect-squares/

http://bookshadow.com/weblog/2015/09/09/leetcode-perfect-squares/

https://leetcode.com/problems/perfect-squares/discuss/71505/Simple-Java-DP-Solution

https://leetcode.com/problems/perfect-squares/discuss/71512/Static-DP-C%2B%2B-12-ms-Python-172-ms-Ruby-384-ms

https://leetcode.com/problems/perfect-squares/discuss/71488/Summary-of-4-different-solutions-(BFS-DP-static-DP-and-mathematics)

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Perfect Squares 完全平方数的更多相关文章

  1. [LeetCode] 0279. Perfect Squares 完全平方数

    题目 Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9 ...

  2. [LintCode] Perfect Squares 完全平方数

    Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...

  3. LeetCode Perfect Squares

    原题链接在这里:https://leetcode.com/problems/perfect-squares/ 题目: Given a positive integer n, find the leas ...

  4. [LeetCode] 279. Perfect Squares 完全平方数

    Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...

  5. 一、Perfect Squares 完全平方数

    一原题 Given a positive integer n, find the least number of perfect square numbers (, , , , ...) which ...

  6. Leetcode279. Perfect Squares完全平方数

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

  7. LeetCode 279. 完全平方数(Perfect Squares) 7

    279. 完全平方数 279. Perfect Squares 题目描述 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n.你需要让组成和的完全平方数 ...

  8. Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares)

    Leetcode之广度优先搜索(BFS)专题-279. 完全平方数(Perfect Squares) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ar ...

  9. 花式求解 LeetCode 279题-Perfect Squares

    原文地址 https://www.jianshu.com/p/2925f4d7511b 迫于就业的压力,不得不先放下 iOS 开发的学习,开始走上漫漫刷题路. 今天我想聊聊 LeetCode 上的第2 ...

随机推荐

  1. 现代3D图形编程学习-关于本书(译)

    本书系列 现代3D图形编程学习 关于这本书 三维图像处理硬件很快成为了必不可少的组件.很多操作系统能够直接使用三维图像硬件,有些甚至要求需要有3D渲染能力的硬件.同时对于日益增加的手机系统,3D图像硬 ...

  2. 自己动手,实现一种类似List<T>的数据结构(一)

    前言 上一篇文章<Unity3D中常用的数据结构总结与分析>简单总结了一下小匹夫工作中经常遇到的一些数据结构.不过小匹夫一直有种观点,就是光说的热闹实际啥也不做真的没啥意思.光说不练假把式 ...

  3. FFmpeg学习5:多线程播放视音频

    在前面的学习中,视频和音频的播放是分开进行的.这主要是为了学习的方便,经过一段时间的学习,对FFmpeg的也有了一定的了解,本文就介绍了 如何使用多线程同时播放音频和视频(未实现同步),并对前面的学习 ...

  4. 代码的坏味道(9)——异曲同工的类(Alternative Classes with Different Interfaces)

    坏味道--异曲同工的类(Alternative Classes with Different Interfaces) 特征 两个类中有着不同的函数,却在做着同一件事. 问题原因 这种情况往往是因为:创 ...

  5. Linux主机上实现树莓派的交叉编译及文件传输,远程登陆

    0.环境 Linux主机OS:Ubuntu14.04 64位,运行在wmware workstation 10虚拟机 树莓派版本:raspberry pi 2 B型. 树莓派OS:官网下的的raspb ...

  6. react-native 简单的导航

    默默潜水了两年了,一直都在看大神们写的博客,现在我也分享一下跟RN导航有关的东西. 前两年我主要是做iOS开发的,现在刚找了份工作,应公司要求,现在开始学习reactnative的东西,由于我以前没怎 ...

  7. .Net语言 APP开发平台——Smobiler学习日志:如何实现快速跳转网页

    Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 样式一 一.跳转网页代码(Button的Click事件) Private Sub Button1_ ...

  8. Checkbox 模板和样式

    <Style TargetType="{x:Type CheckBox}"> <Setter Property="FontFamily" Va ...

  9. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(五)

    (五)注册Website Cloud 1 注册Website Cloud 添加Website Cloud   连接Website Cloud 注意, endpoint 是使用Management Se ...

  10. Java内部类final语义实现

    本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...