题目:
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...
)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12 输出: 3 解释: 12 = 4 + 4 + 4.
输入: n = 13 输出: 2 解释: 13 = 4 + 9.
首先是可以用BFS算法来做:
- count= 1 时,可以走到1*1,2*2,3*3,。。。i*i;将n-1*1,n-2*2,n-3*3....加入队列
- count= 2 时,继续把未访问过的加入队列
当从队列中的值tmp取出来且tmp-i*i==0时,说明,已经按最短路径走到了。输出count即可。
以下是我写的代码:
public static int numSquares(int n){
Boolean[] visited=new Boolean[n+1];
Arrays.fill(visited,false);
Queue<Integer> q=new LinkedList<>();
q.offer(n);
int count=0;
while(!q.isEmpty()){
int size=q.size();
count++;
for(int i=0;i<size;i++){
int tmp=q.poll();
visited[tmp]=true;
for(int j=1;j*j<=n;j++){
if(tmp-j*j==0){
return count;
}else if(tmp-j*j<0){
break;
}else{
if(!visited[tmp-j*j]){
q.offer(tmp-j*j);
}
}
}
}
}
return count;
}
第二种解法是使用动态规划:
状态转移方程是dp[i]=min(dp[i],dp[i-j*j]+1),求得dp[n]即可。代码为:
public static int numSquares(int n){
int[] dp=new int[1000000];
Arrays.fill(dp,10);
dp[0]=0;
for(int i=1;i<=n;i++){
int j=1;
while(j*j<=i){
dp[i]=dp[i]<(dp[i-j*j]+1)?dp[i]:dp[i-j*j]+1;
j++;
}
}
return dp[n];
}
第三种解法是使用数学规律,因为:Lagrange 四平方定理: 任何一个正整数都可以表示成不超过四个整数的平方之和。
所以最多只能是1,2,3,4这4种答案。
算法是如果改数字可以整除4,则一直整除下去直到无法整除为止,此时不会影响count大小,接着是如果该数n%8==7,则返回4,否则测试一下是否可以有两个数组成,然后就只剩3这种答案了。代码如下:
public static int numSquares(int n){
while(n%4==0){
n/=4;
}
if(n%8==7){
return 4;
}
for(int i=1;i*i<=n;i++){
if(i*i==n){
return 1;
}
}
for(int i=1;i*i<=n;i++){
int b=(int)Math.sqrt(n-i*i);
if(b*b+i*i==n){
return 2;
}
}
return 3;
}
这种算法是最快的。还有一种算法是用dfs来做,奈何本人水平不够,数字较小时可以返回正确值,数字较大时就超时了,总共488个测试案例通过了458个,代码也放上去吧。
public static int numSquares(int n) {
int half=(int)Math.sqrt(n);
int count=0;
int[] arr=new int[half+1];
for(int i=1;i<arr.length;i++){
arr[i]=n/(i*i);
count+=arr[i];
}
count=dfs(arr,arr.length-1,arr[arr.length-1],0,0,n,arr[1]);
return count;
}
public static int dfs(int[] arr,int x,int t,int count,int sum,int n,int min){
if(count>=min){
return min;
}
int tmpCount=0;
for(int i=t;i>=0;i--){
sum+=i*x*x;
count+=i;
if(sum==n){
if(count<min){
min=count;
}
return min;
}else if(sum>n){
sum-=x*x*i;
count-=i;
}else{
if(x-1>=1&&count<min){
tmpCount= dfs(arr,x-1,arr[x-1],count,sum,n,min);
if(tmpCount<min){
min=tmpCount;
}
}
sum-=x*x*i;
count-=i;
}
}
return min;
}
整体而言就是这些,以后再改进。