【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 ...
随机推荐
- 【模板】一般图最大匹配(带花树算法)/洛谷P6113
题目链接 https://www.luogu.com.cn/problem/P6113 题目大意 给定一个 \(n\) 个点 \(m\) 条边的无向图,求该图的最大匹配. 题目解析 二分图最大匹配,一 ...
- Hadoop入门 完全分布式运行模式-集群配置
目录 集群配置 集群部署规划 配置文件说明 配置集群 群起集群 1 配置workers 2 启动集群 总结 3 集群基本测试 上传文件到集群 查看数据真实存储路径 下载 执行wordcount程序 配 ...
- A Child's History of England.50
'Knave [man without honor]!' said King Richard. 'What have I done to thee [you] that thou [you] shou ...
- Sharding-JDBC 实现水平分库分表
1.需求分析
- html框架frame iframe
框架 通过使用框架,你可以在同一个浏览器窗口中显示不止一个页面.没分HTML文档称作一个框架. 缺点: 开发人员必须同时跟踪更多的HTML文档 很难打印整张页面 框架结构标签(<frameset ...
- class.getName()和class.getSimpleName()的区别
根据API中的定义: Class.getName():以String的形式,返回Class对象的"实体"名称: Class.getSimpleName():获取源代码中给出的&qu ...
- 用运oracel中的伪列rownum分页
在实际应用中我们经常碰到这样的问题,比如一张表比较大,我们只要其中的查看其中的前几条数据,或者对分页处理数据.在这些情况下我们都需要用到rownum.因此我们要理解rownum的原理和使用方法. Or ...
- 【MySQL】学生成绩
统计每个人的总成绩排名 select stu.`name`,sum(stu.score) as totalscore from stu GROUP BY `name` order by totalsc ...
- Synchronized和Lock接口
关于synchronized字段,不管该关键字是修饰方法还是修饰同步代码块,synchronzed拿到的都是对象. 当synchronized修饰的是方法时,synchronized所拿到的是调用该方 ...
- 8、Redis五大数据类型---哈希(Hash)
一.哈希(Hash)简介: Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象. Redis hash 是一个键值对集合. 二.常用命令 1.h ...