【LeetCode】279. Perfect Squares 解题报告(C++ & Java)
作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/
题目地址:https://leetcode.com/problems/perfect-squares/
题目描述
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.
题目大意
判断一个正整数最少能成为几个数字的平方和。
解题方法
四平方和定理
这道题是考察四平方和定理,说明每个正整数均可表示为4个整数的平方和。
引用自Grandyang大神:
根据四平方和定理,任意一个正整数均可表示为4个整数的平方和,其实是可以表示为4个以内的平方数之和,那么就是说返回结果只有1,2,3或4其中的一个,首先我们将数字化简一下,由于一个数如果含有因子4,那么我们可以把4都去掉,并不影响结果,比如2和8,3和12等等,返回的结果都相同,读者可自行举更多的栗子。
还有一个可以化简的地方就是,如果一个数除以8余7的话,那么肯定是由4个完全平方数组成,这个不会证明。
那么做完两步后,一个很大的数有可能就会变得很小了,大大减少了运算时间,下面我们就来尝试的将其拆为两个平方数之和,如果拆成功了那么就会返回1或2,因为其中一个平方数可能为0. 根据a和b的两个数字是不是0就知道能不能拆成1个或者2个数字的平方和。如果上面这些都不行,那么就是只能3个数字的平方和了。
C++代码如下:
class Solution {
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 = sqrt(n - a * a);
if (a * a + b * b == n) {
return !!a + !!b;
}
}
return 3;
}
};
java代码如下:
public class Solution {
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++) {
for (int b = 0; b * b <= n - a * a; b++) {
if (a * a + b * b == n) {
return (a > 0 ? 1 : 0) + (b > 0 ? 1 : 0);
}
}
}
return 3;
}
}
动态规划
我们定义dp[i]为整数i最少能分解成多少个正整数的平方和。那么有,
dp[i + j * j] = min(dp[i + j * j], dp[i] + 1).
如果一个数x可以表示为一个任意数a加上一个平方数bxb,也就是x=a+bxb,那么能组成这个数x最少的平方数个数,就是能组成a最少的平方数个数加上1(因为b*b已经是平方数了)。
C++代码如下:
class Solution {
public:
int numSquares(int n) {
//dp[i] means how many perfect squres it needs.
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i <= n; ++i) {
for (int j = 1; i + j * j <= n; ++j) {
dp[i + j * j] = min(dp[i + j * j], dp[i] + 1);
}
}
return dp[n];
}
};
Java代码如下:
public static int numSquares(int n) {
int[] dp = new int[n + 1];
// 将所有非平方数的结果置最大,保证之后比较的时候不被选中
Arrays.fill(dp, Integer.MAX_VALUE);
// 将所有平方数的结果置1
for (int i = 0; i * i <= n; i++) {
dp[i * i] = 1;
}
// 从小到大找任意数a
for (int a = 0; a <= n; a++) {
// 从小到大找平方数bxb
for (int b = 0; a + b * b <= n; b++) {
// 因为a+b*b可能本身就是平方数,所以我们要取两个中较小的
dp[a + b * b] = Math.min(dp[a] + 1, dp[a + b * b]);
System.out.println(a + b * b + ":--" + Arrays.toString(dp));
}
}
return dp[n];
}
看下输出就明白了:
0:--[1, 1, 2147483647, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
1:--[1, 1, 2147483647, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
4:--[1, 1, 2147483647, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
9:--[1, 1, 2147483647, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
1:--[1, 1, 2147483647, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
2:--[1, 1, 2, 2147483647, 1, 2147483647, 2147483647, 2147483647, 2147483647, 1, 2147483647]
5:--[1, 1, 2, 2147483647, 1, 2, 2147483647, 2147483647, 2147483647, 1, 2147483647]
10:--[1, 1, 2, 2147483647, 1, 2, 2147483647, 2147483647, 2147483647, 1, 2]
2:--[1, 1, 2, 2147483647, 1, 2, 2147483647, 2147483647, 2147483647, 1, 2]
3:--[1, 1, 2, 3, 1, 2, 2147483647, 2147483647, 2147483647, 1, 2]
6:--[1, 1, 2, 3, 1, 2, 3, 2147483647, 2147483647, 1, 2]
3:--[1, 1, 2, 3, 1, 2, 3, 2147483647, 2147483647, 1, 2]
4:--[1, 1, 2, 3, 1, 2, 3, 2147483647, 2147483647, 1, 2]
7:--[1, 1, 2, 3, 1, 2, 3, 4, 2147483647, 1, 2]
4:--[1, 1, 2, 3, 1, 2, 3, 4, 2147483647, 1, 2]
5:--[1, 1, 2, 3, 1, 2, 3, 4, 2147483647, 1, 2]
8:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
5:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
6:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
9:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
6:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
7:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
10:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
7:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
8:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
8:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
9:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
9:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
10:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
10:--[1, 1, 2, 3, 1, 2, 3, 4, 2, 1, 2]
2
上面的dp是正向推导,可能不利于理解,一般情况下,我们习惯于在求dp[i]的时候,向前面已经有的状态中查找。
这么做的C++代码如下:
class Solution {
public:
int numSquares(int n) {
// dp[i] means how many perfect squres it needs.
vector<int> dp;
dp.push_back(0);
for (int i = 1; i <= n; ++i) {
int val = INT_MAX;
for (int j = 1; j * j <= i; ++j) {
val = min(val, dp[i - j * j] + 1);
}
dp.push_back(val);
}
return dp[n];
}
};
参考资料:
http://segmentfault.com/a/1190000003768736#articleHeader1
http://www.cnblogs.com/grandyang/p/4800552.html
日期
2015/11/15 20:43:42
2016/4/29 21:05:01
2019 年 1 月 7 日 —— 新的一周开始啦啦啊
【LeetCode】279. Perfect Squares 解题报告(C++ & Java)的更多相关文章
- 【LeetCode】367. Valid Perfect Square 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:完全平方式性质 方法二:暴力求解 方法三:二 ...
- leetcode@ [279]Perfect Squares
https://leetcode.com/problems/perfect-squares/ Given a positive integer n, find the least number of ...
- [LeetCode] 279. Perfect Squares 完全平方数
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...
- (BFS) leetcode 279. Perfect Squares
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 1 ...
- [leetcode] #279 Perfect Squares (medium)
原题链接 题意: 给一个非整数,算出其最少可以由几个完全平方数组成(1,4,9,16……) 思路: 可以得到一个状态转移方程 dp[i] = min(dp[i], dp[i - j * j] + ) ...
- [LeetCode 279.] Perfect Squres
LeetCode 279. Perfect Squres DP 是笨办法中的高效办法,又是一道可以被好办法打败的 DP 题. 题目描述 Given a positive integer n, find ...
- 【剑指Offer】不用加减乘除做加法 解题报告(Java)
[剑指Offer]不用加减乘除做加法 解题报告(Java) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...
- LeetCode 1 Two Sum 解题报告
LeetCode 1 Two Sum 解题报告 偶然间听见leetcode这个平台,这里面题量也不是很多200多题,打算平时有空在研究生期间就刷完,跟跟多的练习算法的人进行交流思想,一定的ACM算法积 ...
- 【LeetCode】Permutations II 解题报告
[题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...
随机推荐
- 制作nc文件(Matlab)
首先看一个nc文件中包含哪些部分,例如一个标准的 FVCOM 输入文件 wind.nc: netcdf wind { dimensions: nele = 36858 ; node = 18718 ; ...
- miRNA 基本知识
miRNA MicroRNA (miRNA) 是一类内生的.长度约为20-24个核苷酸的小 RNA,其在细胞内具有多种重要的调节作用.每个 miRNA 可以有多个靶基因的表达,而几个 miRNA 也 ...
- JAVA写入TXT
用java生成txt文件有两种方式: 1)是通过字符流(或字节流): 2)是直接调用PrintWriter类. 具体实现过程如下: 1)字符流(字节流) 代码如下: import java.io.Fi ...
- 非标准的xml解析器的C++实现:一、思考基本数据结构的设计
前言: 我在C++项目中使用xml作为本地简易数据管理,到目前为止有5年时间了,从最初的全文搜索标签首尾,直到目前项目中实际运用的类库细致到已经基本符合w3c标准,我一共写过3次解析器,我自己并没有多 ...
- vivo 敏感词匹配系统的设计与实践
一.前言 谛听系统是vivo的内容审核平台,保障了vivo各互联网产品持续健康的发展.谛听支持审核多种内容类型,但日常主要审核的内容是文本,下图是一个完整的文本审核流程,包括名单匹配.敏感词匹配.AI ...
- 『学了就忘』Linux文件系统管理 — 64、磁盘配额的配置步骤
目录 1.手工建立一个5GB的分区 2.建立需要做限制的三个用户 3.在分区上开启磁盘配额功能 4.建立磁盘配额的配置文件 5.开始设置用户和组的配额限制 6.启动和关闭配额 7.磁盘配额的查询 8. ...
- Sharding-JDBC 实现水平分库分表
1.需求分析
- 一道题目学ES6 API,合并对象id相同的两个数组对象
var arr2=[{id:1,name:'23'}] var arr1=[{id:1,car:'car2'}] const combined = arr2.reduce((acc, cur) =&g ...
- Vue相关,vue.nextTick
vue中有一个较为特殊的API,nextTick.根据官方文档的解释,它可以在DOM更新完毕之后执行一个回调,用法如下: // 修改数据 vm.msg = 'Hello' // DOM 还没有更新 V ...
- LVS nat模型+dr模型
nat模型 在 rs1 和 rs2 安装httpd 并配置测试页,启动 [root@rs1 ~]# yum install httpd -y[root@rs1 ~]# echo "thi ...