题目十一:
给一个正整数 n, 找到若干个完全平方数(比如1, 4, 9, ... )使得他们的和等于 n。你需要让平方数的个数最少。
样例
给出 n = 12
, 返回 3
因为 12 = 4 + 4 + 4
。
给出 n = 13
, 返回 2
因为 13 = 4 + 9
。
代码:
class Solution {
public:
/*
* @param n: a positive integer
* @return: An integer
*/
int count;
int numSquares(int n) {
// write your code here
int i,j=0;
int a[n];
int num,min = n;
count = 0;
for(i = n;i>0;i--)
{
if(Or(i)!=0)
{
a[j] = Or(i);
j++;
}
}
num = j;
for(i = 0;i<num;i++)
{
count = Get(a,n,i,num);
if(count<min)
{
min = count;
}
count = 0;
}
return min;
}
int Or(int num)
{
int a = sqrt(num);
if(num == a*a)
{
return num;//是完全平方数
}
else
{
return 0;
}
}
int Get(int a[],int num,int i,int length)
{
int num1 = num-a[i];
if(num1 == 0)
{
count++;
return count;
}
else
{
for(;i<length-1;i++)
{
if(num1>=a[i])
{
break;
}
else if(num1<a[i]&&num1>=a[i+1])
{
i = i+1;
break;
}
}
count++;
return Get(a,num1,i,length);
}
}
};
真是内牛满面啊,本来半个小时要求的题目我想了整整三个多小时!emmmm,标签是有动态规划,但是,我没学过动态规划,所以百度上的代码也没看明白,然后我选择自立更生。
算法的大致思路:数字分成两类,一类是完全平方数,另一类是非完全平方数,非完全平方数可以分解成一个完全平方数和一个非完全平方数(这里是我在百度上学到的,但是后来的思路没看懂,不知道和我的是不是一样)
1、找到小于n的所有的完全平方数(用函数Or实现,先开平方根,然后在平方,如果得到的数与原来相同,就是完全平方数。比如5开平方是2.2左右,转换成int型是2,再平方是4!=5所以5不是完全平方数)
2、寻找所有的可能的count的值:递归的把传入Get函数中的num变成一个非完全平方数+小于num的最大的完全平方数的值,计算count的值与min进行比较,最后得到最小的min然后作为结果返回。
3、
numSquares函数与Get函数的思路不同:
numSquares
函数:我的想法是这样的,如果要求最小个数的话,那n分解出的完全平方数肯定要从最大的开始,但是存在像12这种是某个完全平方数的倍数的数而且这个倍数恰好就是最小个数且这个完全平方数不是小于12的最大的完全平方数,所以要依次把n-a[i]
作为参数num传入Get函数中。(为了确定最小个数时最大的完全平方数)
Get函数:递归的把num变成一个非完全平方数+小于等于num的最大的完全平方数的值,把非完全平方数作为新的num参数传入
实际上这个代码中还存在问题,如果最小个数的产生不由完全平方数决定而是由它分离出的非完全平方数决定,那这个代码的错误率还是很高的,咳咳,竟然Accepted了我也是很惊讶,处理的方法很简单,递归的把
非完全平方数也找最小个数就好了吧,很可能会超时,运算量又增加了很多······
这是之前我没看懂的代码,现在大概是理解了
public class Solution {
/*
* @param n: a positive integer
* @return: An integer
*/
public int numSquares(int n) {
// write your code here
int[] dp = new int[n+1]; //对应的角标分解成最小完全平方数的最小个数
Arrays.fill(dp, Integer.MAX_VALUE); //dp[]数组中所有的数据都置Integer.MAX_VALUE(int可以表示的最大数据)
for(int i = 0; i * i <= n; i++)
dp[i * i] = 1; //小于等于n的所有的完全平方数的最小个数都是1
for(int i = 1; i <= n; i++) //看成是一个普通数(i)+完美平方数(j*j)
{ //选定第一个数为 i
for(int j = 1; i + j * j <= n; j++)
{ //选定另一个数为 j*j
dp[i + j * j] = Math.min(dp[i] + 1, dp[i + j * j]); //从小到大查找
}
}
return dp[n];
}
}
数组dp[]:用来存储对应角标的能够分解成完全平方数的最小个数
对于本身就是完全平方数的数据i来说,dp[i]为1(比如dp[1] = 1,dp[4]=1,dp[9]=1,dp[16]=1),非完全平方数i,dp[i]=
Integer.MAX_VALUE
一个普通数n可以分解成一个普通数i和一个完全平方数j*j,即n=i+j*j,那么n的当前最小个数=i的最小个数+1,然后遍历所有的i+j*j的组合不断地刷新<=n的所有数字的最小个数,最后得到n的最小个数。
感觉这个思路的出发点和我刚好相反,我是从最大的数据出发,这个是从最小的数据出发依次寻找,比较详细一点,显示的时间消耗要远大于我的,但是准确度比我的更高。这是原文章(
点击打开链接),并没有很多的解释,我开始完全没看懂的说。