:-: 4.4 完全平方数
*****
**题干**
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
```
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
```
示例 2:
```
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
```
**题目分析:**
该题目可以用四平方和定理求解,也可动态规划求解,也可以当作最短路径来处理。
**新手有可能遇到的解题思路陷阱:**
陷入了贪心做法不可自拔,这道题不能使用贪心来求解,比如12用贪心求解12 = 9 + 1 + 1 +1,其实应该是12 = 4 + 4 + 4。不知道四平方和定理以及不知道由四平方和定理推导出的各个推导,和小技巧。
**解题思路分析以及代码实现:**
第一种思路:四平方和定理:
[四平方和定理——维基百科](https://zh.wikipedia.org/wiki/%E5%9B%9B%E5%B9%B3%E6%96%B9%E5%92%8C%E5%AE%9A%E7%90%86)
[Lagrange 四平方和定理](https://www.changhai.org/articles/science/mathematics/four_square_theorem.php)
任何一个正整数都可以表示成不超过四个整数的平方之和。也就是说我们的返回值是[1,2,3,4]中的一个值。首先我们看两个得到导论:1.一个数如果含有因子4,我们可以将这个数除以4,用此所得数求并不影响结果,比如2和8或3和12等等,它们的结果相同;2.一个数除以8余7的话,则这个数由4个完全平方数组成。证明:满足![](https://box.kancloud.cn/15386dcd5d6d171cb18159b035e95235_99x26.png)此类的数由4个完全平方数组成
[不能写成3个平方数之和的正整数 ](https://bbs.emath.ac.cn/thread-3063-1-1.html)
[n=(8k+7)4^m(k,m 为非负整数)不能表示为三个平方数之和](http://www.mathchina.net/dvbbs/dv_rss.asp?s=xhtml&boardid=7&id=2798&page=51)
[欧拉定理,费马小定理、Lagrange定理](http://www.cis.umac.mo/~fstitl/10mmo/sum-4squares.html)
第一种思路代码:
```
public int numSquares(int n) {
while (n % 4 == 0)
n /= 4;
if (n % 8 == 7)
return 4;
for (int a = 0; a * a <= n; ++a) {
int b = (int) Math.sqrt(n - a * a);
if (a * a + b * b == n) {
if (a == 0) {
return 1;
}
return 2;
}
}
return 3;
}
```
**复杂度分析:**
时间复杂度:O(sqrt(n))。
空间复杂度:O(1)。
第二种思路:动态规划,首先我们就要找出动态转移方程,我不是大佬,只是个菜鸡,只能用笨方法找规律来总结动态转移方程,我们可以有两种思路:
**自顶向下**:首先我们需要知道初始F(n) = n(n = 1 + 1 + 1 +......+ 1),题目中说n是由完全平方数组成的,那么n可以拆分一个完全平方数i^2与n-i^2两部分,然后可以得出n = i^2 + (n - i^2)(**i的取值范围为[1, (int)sqrt(n)]**),F(n) = min(n, 1 + F(n - i^2)) => min(1 + F(n - i^2), 1 + F(n - (i + 1)^2)),然后F(n - i^2)等等再分解,这样的话动态转移方程:**F(n) =min(n, 1 + F(n - i^2)) => min(1 + F(n - i^2), 1 + F(n - (i + 1)^2))**。
**自底向上**:首先我们需要知道初始F(n) = n(n = 1 + 1 + 1 +......+ 1),F(0) = 0,F(1) = min(1, 1 + F(1 - 1^2)) = 1(1可以拆分成1^2 +(1 - 1^2)), F(2) = min(2, 1 + F(2 - 1^2)) = 2(2可以拆分为1^2 + 2 - 1^2),F(3) = min(3, 1 + F(3 - 1^2)) = 3,F(4) = min(4, 1 + F(4 - 1^2)) = min(1 + F(4 - 1^2, 1 + F(4 - 2^2)) = 1,以此类推,得出状态转移方程为:**F(n) =min(n, 1 + F(n - i^2)) => min(1 + F(n - i^2), 1 + F(n - (i + 1)^2)),i的取值范围为[1, (int)sqrt(n)]**。
第二种思路代码:
~~~
public static int numSquares(int n) {
int[] dp = new int[n + 1];
for (int i = 1; i <= n; i++) {
dp[i] = i;
int top = (int) Math.sqrt(i);
for (int j = 1; j <= top; j++) {
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
~~~
时间复杂度:O(n*sqrt(n))。
空间复杂度:O(n)。
**若有理解错误的、写错的地方、更好的思路,方法,望各位读者评论或者发邮件指正或指点我,不胜感激。**
- 前言
- 第一部分 初级入门算法
- 第一章 数组
- 1.1 删除排序数组中的重复项
- 1.2 删除排序数组中的重复项 II
- 1.3 买卖股票的最佳时机
- 1.4 买卖股票的最佳时机 II
- 1.5 移动零
- 1.6 区间子数组个数
- 1.7 搜索插入位置
- 1.8 合并两个有序数组
- 1.9 两个数组的交集
- 第二章 哈希表
- 2.1 两数之和
- 2.2 错误的集合
- 2.3 翻转卡片游戏
- 2.4 有效的字母异位词
- 第三章 链表
- 第四章 数学
- 4.1 加一
- 4.2 反转整数
- 4.3 排列硬币
- 4.4 完全平方数
- 4.5 消除游戏
- 第五章 双指针
- 第六章 字符串
- 6.1 整数转罗马数字
- 6.2 罗马数字转整数
- 6.3 反转字符串
- 6.4 压缩字符串
- 6.5 验证回文串
- 6.6 长按键入
- 6.7 字符串中的第一个唯一字符
- 第七章 二分查找
- 7.1 猜数字大小
- 第八章 分治算法
- 第九章 动态规划
- 9.1 爬楼梯
- 9.2 使用最小花费爬楼梯
- 9.3 打家劫舍
- 9.4 打家劫舍 II
- 9.5 第N个泰波那契数
- 第十章 回溯算法
- 第十一章 栈
- 11.1 棒球比赛
- 第十二章 堆
- 12.1 数组中的第K个最大元素
- 第十三章 贪心算法
- 第十四章 排序
- 14.1 冒泡排序
- 14.2 鸡尾酒排序
- 14.3 选择排序
- 14.4 插入排序
- 14.5 折半插入排序
- 14.6 希尔排序
- 14.7 快速排序
- 14.8 树形选择排序
- 14.9 堆排序
- 第十五章 位运算
- 15.1 只出现一次的数字
- 第十六章 思维题
- 16.1 TinyURL 的加密与解密
- 16.2 灯泡开关
- 16.3 字母板上的路径
- 第十七章 队列
- 17.1 扑克牌顺序