完全平方数
动态规划法(原始版)
举例:
- dp[12]=dp[9]+dp[3]
- dp[9]=1
- dp[3]=dp[1]+dp[2]
- dp[1]=1
- dp[2]=dp[1]+dp[1]=2
- dp[12]=dp[4]+dp[8]
- dp[4] =1
- dp[8]=dp[4]+dp[4]=2
- 定义dp数组,dp[i]表示i可以由最少几个完全平方数表示
- 初始化,dp[i*i]=1,完全平方数都可以由自己 1个数表示
- 递推公式,从举例中我们可以看到,dp[12]=dp[9]+dp[3],如果我们从小往大了遍历,那dp[9]和dp[3]其实已经知道了,不用再重复计算。
- 遍历,假如是 n = 12 n=12 n=12,dp[12]=dp[j]+dp[i-j],比如 d p [ 12 ] = d p [ 1 ] + d p [ 11 ] dp[12]=dp[1]+dp[11] dp[12]=dp[1]+dp[11],一直遍历到 i = = i − j i==i-j i==i−jdp[12]=dp[6]+dp[6],因为再往后遍历会重复, d p [ 12 ] = d p [ 11 ] + d p [ 1 ] dp[12]=dp[11]+dp[1] dp[12]=dp[11]+dp[1]
- 手写一遍 n = 12 n=12 n=12这种情况
public static int numSquares(int n) {
int[]dp=new int[n+1];
for (int i = 1; i < n+1; i++) {
dp[i]=i;
}
for(int i=1;i*i<n+1;i++){
dp[i*i]=1;
}
for(int i=2;i<=n;++i){
for(int j=1;j<=i-j;j++){
dp[i]=Math.min(dp[i],dp[j]+dp[i-j]);
}
}
return dp[n];
}
完全背包
物品:每个完全平方数(可以拿好几次)
public int numSquares(int n) {
List<Integer> nums = new ArrayList();
for (int i = 1; i * i <= n; ++i) nums.add(i * i); // 把可选的物品算出来
int[] dp = new int[n + 1];
Arrays.fill(dp, n + 1);
dp[0] = 0;
for (int i = 0; i < nums.size(); ++i) {
for (int j = nums.get(i); j < n + 1; ++j) {
dp[j] = Math.min(dp[j], dp[j - nums.get(i)] + 1);
}
}
return dp[n];
}
四平方和定理
任意一个正整数都可以用最多四个完全平方数表示
也就是返回值只可能为 1、2、3、4
如果n为完全平方数则返回
如果n满足条件:
n
=
4
k
×
(
8
m
+
7
)
n=4^k\times(8m+7)
n=4k×(8m+7),则返回4
如果n减去一个完全平方数 得一个完全平方数:
n
−
b
2
=
a
2
n-b^2=a^2
n−b2=a2,则返回2
不满足以上三种条件则返回3
public int numSquares(int n) {
if (isPerfectSquare(n)) {
return 1;
}
if (checkAnswer4(n)) {
return 4;
}
for (int i = 1; i * i <= n; i++) {
int j = n - i * i;
if (isPerfectSquare(j)) {
return 2;
}
}
return 3;
}
// 判断是否为完全平方数
public boolean isPerfectSquare(int x) {
int y = (int) Math.sqrt(x);
return y * y == x;
}
// 判断是否能表示为 4^k*(8m+7)
public boolean checkAnswer4(int x) {
while (x % 4 == 0) {
x /= 4;
}
return x % 8 == 7;
}