以前总是不太知道什么题需要用到动态规划,这个题加深了我对动规的理解。先说说我刚开始的思路,我想的是找到比n小的最近的完全平方数,从它开始遍历,就拿12来说,先找到离她最近的完全平方数9,然后12-9=3,这个时候就需要再重新找比3最小的完全平方数,再进行这个循环,所以想要得到组成12最少的完全平方数的个数的时候,既要找组成9最少的完全平方的个数,然后找9的也需要找3的……所以就能推导出来,后面的最优解需要从前面的最优解得到,所以才要使用动规。
class Solution {
public int numSquares(int n) {
int[] dp = new int[n + 1]; // 默认初始化值都为0
for (int i = 1; i <= n; i++) {
dp[i] = i; // 最坏的情况就是每次+1
for (int j = 1; i - j * j >= 0; j++) {
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
// 动态转移方程,j*j表示完全平方数
}
}
return dp[n];
}
}
然后再来看看这个题的dp数组,dp[i]要初始化为i,因为组成一个数最坏的情况就是全都是1,然后dp[i]表示组成i需要的最少的完全平方数的个数。
dp[i] 可以由dp[i- j * j]推出, dp[i - j * j] + 1 便可以凑成dp[i]。
此时我们要选择最小的dp[j],所以递推公式:dp[j] = min(dp[j],dp[j - i * i] + 1);
以dp[4]为例,它可以分为1,dp[3],或者2,dp[0],所以dp[4]=min(dp[4],dp[4 -1 * 1]+1)
dp[4]=min(dp[4],dp[4-2 * 2]+1)
现在按照代码走一遍12,
dp[1]初始化为1,dp[1]=min(dp[1],dp[1-1* 1 ]+1)中最小的,dp[1-11]+1=1,所以dp[1]=1;
dp[2]初始化为2,然后进入循环,dp[2]=min(dp[2],dp[2-1 * 1]+1),dp[2-1 * 1]=dp[1]=1,然后再加个1,得到2,所以dp[2]=2,然后j++=2,2-2 * 2<0,所以不进循环了,最终dp[2]=2;
dp[3]初始化为3,进入循环,dp[3]=min(dp[3],dp[3-1 * 1]+1),dp[3-1 * 1]=dp[2]=2,再加1,所以dp[3]=3,然后j++=2,3-2 * 2<0,不进循环,最终dp[3]=3
dp[4]初始化为4,进入循环,dp[4]=min(dp[4],dp[4-1 * 1]+1),dp[4-1 * 1]=dp[3]=3,再加1,dp[4]=4,j++,4-2 * 2=0,进入循环,dp[4]=min(dp[4],dp[4-2 * 2]+1),dp[4-22]=dp[0],=0,再加一,dp[4]=1,然后就不进循环了,最后dp[4]=1;
dp[5]初始化为5,进入循环,dp[5]=min(dp[5],dp[5-1 * 1]+1)
dp[5-1 * 1]=dp[4]=1,再加1,d[5]=2,然后再进入循环,dp[5]=min(dp[5],dp[5-2 * 2]+1),dp[5-2 * 2]=dp[1]=1,再加1,dp[5]=2;
dp[6]初始化为6,dp[6]=min(dp[6],dp[6-1 * 1]+1),dp[6-1 * 1]=dp[5]=2,再加1,dp[6]=3,再进入循环,dp[6]=min(dp[6],dp[6-2 * 2]+1),dp[6-2 * 2]=dp[2]=2,再加1,所以d[6]=3
……