我无法理解其中一个Leetcode问题。
给定正整数n,找到总和为n的最小正方数(例如,1,4,9,16 ......)。
例如,给定n = 12,返回3,因为12 = 4 + 4 + 4;给定n = 13,返回2,因为13 = 4 + 9。
解决方案:
int numSquares(int n) {
static vector<int> dp {0};
while (dp.size() <= n) {
int m = dp.size(), squares = INT_MAX;
for (int i=1; i*i<=m; ++i)
squares = min(squares, dp[m-i*i] + 1);
dp.push_back(squares);
}
return dp[n];
}
我真的不明白min(squares,dp[m-i*i]+1)
发生了什么。你能解释一下吗?
THX。
答案 0 :(得分:6)
您提到的解决方案是自下而上的算法版本。为了更好地理解算法,我建议查看解决方案的自上而下版本。
让我们仔细观察计算最小正方形量的递归关系,包含在数字N
内。对于给定的N
和任意数字x
(被认为是最短数字序列的成员,其完美平方总和为N
):
f(N, x) = 0 , if N = 0 ;
f(N, x) = min( f(N, x + 1), f(N - x^2, 1) ) , if N >= x^2 ;
f(N, x) = +infinity , otherwise ;
solution(N) = f(N, 1)
现在,考虑到所考虑的重复,我们可以构建自上而下的解决方案(我将在Java中实现它):
int solve(int n) {
return solve(n, 1);
}
int solve(int n, int curr) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
// if curr belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int inclusive = solve(n - (curr * curr), 1) + 1;
// otherwise:
int exclusive = solve(n, curr + 1);
return Math.min(exclusive, inclusive);
}
给定解决方案的运行时复杂性是指数级的。
但是,我们注意到[1..n]
的{{1}}和n
值只有[1..sqrt(n)]
个可能值。这意味着,函数curr
的参数的不同值只有n * sqrt(n)
组合。因此,我们可以创建memoization表并降低自上而下解决方案的复杂性:
solve
鉴于解决方案具有运行时复杂性int solve(int n) {
// initialization of the memoization table
int[][] memoized = new int[n + 1][(int) (Math.sqrt(n) + 1)];
for (int[] row : memoized) {
Arrays.fill(row, NOT_INITIALIZED);
}
return solve(n, 1, memoized);
}
int solve(int n, int curr, int[][] memoized) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
if (memoized[n][curr] != NOT_INITIALIZED) {
// the sub-problem has been already solved
return memoized[n][curr];
}
int exclusive = solve(n, curr + 1, memoized);
int inclusive = solve(n - (curr * curr), 1, memoized) + 1;
memoized[n][curr] = Math.min(exclusive, inclusive);
return memoized[n][curr];
}
。
但是,可以将运行时复杂度降低到O(N * sqrt(N))
。
至于O(N)
的递归关系仅取决于f(N, x)
和f(N, x + 1)
- 这意味着关系可以等效转换为循环形式:
f(N - x^2, 1)
在这种情况下,我们必须仅为其f(0) = 0
f(N) = min( f(N - x^2) + 1 ) , across the all x, such that x^2 <= N
个不同的参数值记住f(N)
。
因此,下面介绍了N
自上而下的解决方案:
O(N)
最后,所提出的自上而下的解决方案可以轻松转换为自下而上的解决方案:
int solve_top_down_2(int n) {
int[] memoized = new int[n + 1];
Arrays.fill(memoized, NOT_INITIALIZED);
return solve_top_down_2(n, memoized);
}
int solve_top_down_2(int n, int[] memoized) {
if (n == 0) {
return 0;
}
if (memoized[n] != NOT_INITIALIZED) {
return memoized[n];
}
// if 1 belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int result = solve_top_down_2(n - (1 * 1)) + 1;
for (int curr = 2; (curr * curr) <= n; curr++) {
// check, whether some other number belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
result = Math.min(result, solve_top_down_2(n - (curr * curr)) + 1);
}
memoized[n] = result;
return result;
}
答案 1 :(得分:5)
我也很难过。我们以n = 13为例。
所以我们留下来比较dp(4),dp(9)和dp(12)中最小的一个。因此最小。
答案 2 :(得分:0)
澄清你的困惑在于问题本身。结构dp
包含最小数量的正方形,它总计到dp
的索引位置。
例如,squares
会在3
时返回n=9
,但最不可能是1
,这是dp[m- i*i] + 1
将返回的内容。