作者: 负雪明烛
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)的更多相关文章

  1. 【LeetCode】367. Valid Perfect Square 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:完全平方式性质 方法二:暴力求解 方法三:二 ...

  2. leetcode@ [279]Perfect Squares

    https://leetcode.com/problems/perfect-squares/ Given a positive integer n, find the least number of ...

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

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

  4. (BFS) leetcode 279. Perfect Squares

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

  5. [leetcode] #279 Perfect Squares (medium)

    原题链接 题意: 给一个非整数,算出其最少可以由几个完全平方数组成(1,4,9,16……) 思路: 可以得到一个状态转移方程  dp[i] = min(dp[i], dp[i - j * j] + ) ...

  6. [LeetCode 279.] Perfect Squres

    LeetCode 279. Perfect Squres DP 是笨办法中的高效办法,又是一道可以被好办法打败的 DP 题. 题目描述 Given a positive integer n, find ...

  7. 【剑指Offer】不用加减乘除做加法 解题报告(Java)

    [剑指Offer]不用加减乘除做加法 解题报告(Java) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...

  8. LeetCode 1 Two Sum 解题报告

    LeetCode 1 Two Sum 解题报告 偶然间听见leetcode这个平台,这里面题量也不是很多200多题,打算平时有空在研究生期间就刷完,跟跟多的练习算法的人进行交流思想,一定的ACM算法积 ...

  9. 【LeetCode】Permutations II 解题报告

    [题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...

随机推荐

  1. Go知识点大纲

    目录 1. 基本介绍 2. 安装及配置 3. 变量 4. 常量 5. 数据类型 5.1 numeric(数字) 5.2 string(字符串) 5.3 array(数组) 5.4 slice(切片) ...

  2. Mysql-单个left join 计算逻辑(一对多问题)

    BUG背景: 我们有一个订单表 和 一个 物流表 它们通过 订单ID 进行一对一的关系绑定.但是由于物流表在保存订单信息的时候没有做判断该订单是否已经有物流信息,这就变成同一个订单id在物流表中存在多 ...

  3. 中兴交换机基础配置(备份、dhcp中继、monitor)

    1. 备份配置 格式: copy tftp/sftp/ftp [vrf mng] root: 本地文件 远端文件 1. 通过tftp进行备份,vrf mng表示指定使用管理口链路连接 copy tft ...

  4. Docker网络设置及文件挂载

    网络设置–net=bridge- 默认选项,用网桥的方式来连接docker容器.–net=host- docker跳过配置容器的独立网络栈.–net=container:NAME_or_ID- 告诉d ...

  5. HMS Core Discovery直播预告 | AI画质增强 ,开启超清视界

    [直播入口] B站华为开发者联盟:http://live.bilibili.com/22551651 4K.8K视频屡见不鲜,HD.FHD分辨率成小屏标配,当网络卡顿.视频自动切换到较低画质时,用户最 ...

  6. R2CNN模型——用于文本目标检测的模型

    引言 R2CNN全称Rotational Region CNN,是一个针对斜框文本检测的CNN模型,原型是Faster R-CNN,paper中的模型主要针对文本检测,调整后也可用于航拍图像的检测中去 ...

  7. 学习java 7.13

    学习内容: 一个汉字存储:如果是GBK编码,占用2个字节:如果是UTF-8编码,占用3个字节 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数 字符流=字节流+编码表 采用何种规则编码,就要 ...

  8. C++ 成绩排名

    1004 成绩排名 (20分)   读入 n(>)名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式: 每个测试输入包含 1 个测试用例,格式为 第 1 行:正整数 ...

  9. javaIO——输入输出流

    字节流与字符流 File类不支持对文件内容进行相关的操作,所有若要处理文件的内容,则需要通过流操作模式来完成. 流的基本操作步骤: Step1:根据文件路径创建File类对象. Step2:根据字节流 ...

  10. Linux基础命令---ab测试apache性能

    ab ab指令是apache的性能测试工具,它可以测试当前apache服务器的运行性能,显示每秒中可以处理多少个http请求. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.F ...