看题解:我明白了 我是草履虫
1.题目
2.方法一:动态规划
分析
代码
class Solution {
public int numSquares(int n) {
int ssize=(int)Math.sqrt(n)+1;
int[] snums=new int[ssize];
//snums存着每个数的平方数值
for(int i=1;i<ssize;i++){
snums[i]=i*i;
}
int[] dp=new int[n+1];
//初始化dp数组每个值为极大值
Arrays.fill(dp, Integer.MAX_VALUE);
//n=0时,结果是0个平方数
dp[0]=0;
//1..n,逐个计算dp[i]
for(int i=1;i<=n;i++){
for(int j=1;j<ssize;j++){
if(snums[j]>i){
break;dp[i]的更新结束了,因为后面的平方数大于所需
}
dp[i]=Math.min(dp[i],dp[i-snums[j]]+1);
}
}
return dp[n];
}
}
复杂度
时间复杂度:O(n* √n),在主步骤中,我们有一个嵌套循环,其中外部循环是 n次迭代,而内部循环最多需要 √n。
空间复杂度:O(n),使用了一个一维数组 dp。
结果
3.方法二:贪心枚举
分析
定义一个方法,is_divided_by(n, count),返回n是否能被分解为count个平方数。分解终止时,即count=1时,只需要检查一个数n是否是平方数(是否在平方数数组里)。
与方法一 一样,需要存储1…Math.sqrt(n)+1的平方数。由于只需要知道它是否在平方数数组里,用HashSet存储平方数能降低时间复杂度。
代码
class Solution {
//创建存储平方数的数组
HashSet<Integer> set;
public int numSquares(int n) {
set=new HashSet<>();
//平方数数组 初始化赋值
int ssize=(int)Math.sqrt(n)+1;
for(int i=1;i<=ssize;i++){
set.add(i*i);
}
int count=1;
for(count=1;count<=n;count++){
//n能被拆分成count个平方数
if(is_divided_by(n, count)){
return count;
}
}
return count;
}
boolean is_divided_by(int n,int count){
//递归终止条件
if(count==1){
return set.contains(n);
}
for(Integer s:set){
//
if(is_divided_by(n-s,count-1)){
return true;
}
}
return false;
}
}
复杂度
结果
4.方法三:贪心+BFS
好家伙 真学不动了 每题都不会 每题都多解 脑子是个好东西我也想有
分析
复杂度
代码
class Solution {
public int numSquares(int n) {
//存储平方数
//这次必须是有序的,所以用ArrayList数组,我本来用Set解答错误
ArrayList<Integer> square_nums=new ArrayList<>();
for(int i=1;i*i<=n;i++){
square_nums.add(i*i);
}
//队列
HashSet<Integer> queue=new HashSet<>();
//初始化队列
queue.add(n);
//计数
int value=0;
while(queue.size()>0){
value++;
HashSet<Integer> next_queue=new HashSet<>();
for(Integer a:queue){
for(Integer b:square_nums){
if(a<b){
break;
}
//队列中的数是平方数:已找到解,返回value
else if(a.equals(b)){
return value;
}
//队列中的数不是平方数:减去平方数,并加入下一轮队列
else{
next_queue.add(a-b);
}
}
}
//迭代队列
queue=next_queue;
}
return value;
//其实根本走不到这一步,循环里一定返回了,所以这里返回啥都行
}
}
结果
5.方法四:数学方法
四平方定理blablabla…略过