[英]Rebuild an array of integers after summing the digits of each element
We have an strictly increasing array of length n
( 1 < n < 500).我们有一个长度为
n
(1 < n < 500)的严格递增数组。 We sum the digits of each element to create a new array with each elements values is in range 1 to 500.The task is to rebuild the old array from the new one.我们将每个元素的数字相加以创建一个新数组,每个元素的值在 1 到 500 之间。任务是从新数组重建旧数组。 since there might be more than one answer, we want the answers with the minimum value of the last element.
由于可能有多个答案,我们希望答案具有最后一个元素的最小值。 Example:
例子:
3 11 23 37 45 123 =>3 2 5 10 9 6
3 11 23 37 45 123 =>3 2 5 10 9 6
now from the second array, we can rebuild the original array in many different ways for instance:现在从第二个数组,我们可以用许多不同的方式重建原始数组,例如:
12 20 23 37 54 60
12 20 23 37 54 60
from all the possible combinations, we need the one we minimum last element.从所有可能的组合中,我们需要最小化最后一个元素。
My Thoughts so far:到目前为止我的想法:
The brute force way is to find all possible permutations to create each number and then create all combinations possible of all numbers of the second array and find the combination with minimum last element.蛮力方法是找到所有可能的排列来创建每个数字,然后创建第二个数组的所有数字的所有可能组合,并找到最后一个元素最少的组合。 It is obvious that this is not a good choice.
很明显,这不是一个好的选择。
Using this algorithm(with exponential time.) we can create all possible permutations of digits that sum to a number in the second arrays. Note that we know the original elements were less than 500 so we can limit the death of search of the algorithm.使用此算法(使用指数时间),我们可以创建所有可能的数字排列,总和为第二个数字 arrays。请注意,我们知道原始元素少于 500,因此我们可以限制算法搜索的死亡。
One way I thought of that might find the answer faster is to:我想到的一种可能更快找到答案的方法是:
I think this is a greed solution but I'm not very sure about the time complexity.我认为这是一个贪婪的解决方案,但我不太确定时间复杂度。 Also I want to know is there a better solution for this problem?
另外我想知道这个问题有更好的解决方案吗? like using
dp
?喜欢用
dp
吗?
For simplicity, let's have our sequence 1
-based and the input sequence is called x
.为简单起见,让我们以序列
1
为基础,输入序列称为x
。
We will also use an utility function, which returns the sum of the digits of a given number:我们还将使用实用程序 function,它返回给定数字的数字总和:
int sum(int x) {
int result = 0;
while (x > 0) {
result += x % 10;
x /= 10;
}
return result;
}
Let's assume that we are at index idx
and try to set there some number called value
(given that the sum of digits of value
is x[idx]
).让我们假设我们在索引
idx
并尝试在那里设置一些称为value
的数字(假设value
的数字总和为x[idx]
)。 If we do so, then what could we say about the previous number in the sequence?如果我们这样做,那么对于序列中的前一个数字我们能说些什么呢? It should be strictly less than
value
.它应该严格小于
value
。
So we already have a state for a potential dp
approach - [idx, value]
, where idx
is the index where we are currently at and value
denotes the value we are trying to set on this index.所以我们已经有一个 state 用于潜在的
dp
方法 - [idx, value]
,其中idx
是我们当前所在的索引, value
表示我们试图在此索引上设置的值。
If the dp
table holds boolean
values, we will know we have found an answer if we have found a suitable number for the first number in the sequence.如果
dp
表包含boolean
值,如果我们为序列中的第一个数字找到了合适的数字,我们就会知道我们已经找到了答案。 Therefore, if there is a path starting from the last row in the dp
table and ends at row 0
then we'll know we have found an answer and we could then simply restore it.因此,如果有一条路径从
dp
表的最后一行开始到第0
行结束,那么我们就会知道我们找到了答案,然后我们可以简单地恢复它。
Our recurrence function will be something like this:我们的循环 function 将是这样的:
f(idx, value) = OR {dp[idx - 1][value'], where sumOfDigits(value) = x[idx] and value' < value}
f(0, *) = true
Also, in order to restore the answer, we need to track the path.此外,为了恢复答案,我们需要跟踪路径。 Once we set any
dp[idx][value]
cell to be true, then we can safe the value'
to which we would like to jump in the previous table row.一旦我们将任何
dp[idx][value]
单元格设置为 true,那么我们就可以保护我们想要在前一个表行中跳转到的value'
。
Now let's code that one.现在让我们编写代码。 I hope the code is self-explanatory:
我希望代码是不言自明的:
boolean[][] dp = new boolean[n + 1][501];
int[][] prev = new int[n + 1][501];
for (int i = 0; i <= 500; i++) {
dp[0][i] = true;
}
for (int idx = 1; idx <= n; idx++) {
for (int value = 1; value <= 500; value++) {
if (sum(value) == x[idx]) {
for (int smaller = 0; smaller < value; smaller++) {
dp[idx][value] |= dp[idx - 1][smaller];
if (dp[idx][value]) {
prev[idx][value] = smaller;
break;
}
}
}
}
}
The prev
table only keeps information about which is the smallest value'
, which we can use as previous to our idx
in the resulting sequence. prev
表只保留关于哪个是最小值value'
信息,我们可以在结果序列中将其用作我们的idx
之前的信息。
Now, in order to restore the sequence, we can start from the last element.现在,为了恢复序列,我们可以从最后一个元素开始。 We would like it to be minimal, so we can find the first one that has
dp[n][value] = true
.我们希望它是最小的,所以我们可以找到第一个
dp[n][value] = true
。 Once we have such element, we then use the prev
table to track down the values up to the first one:一旦我们有了这样的元素,我们就可以使用
prev
表来追踪到第一个元素的值:
int[] result = new int[n];
int idx = n - 1;
for (int i = 0; i <= 500; i++) {
if (dp[n][i]) {
int row = n, col = i;
while (row > 0) {
result[idx--] = col;
col = prev[row][col];
row--;
}
break;
}
}
for (int i = 0; i < n; i++) {
out.print(result[i]);
out.print(' ');
}
If we apply this on an input sequence:如果我们将其应用于输入序列:
3 2 5 10 9 6
we get我们得到
3 11 14 19 27 33
The time complexity is O(n * m * m)
, where n
is the number of elements we have and m
is the maximum possible value that an element could hold.时间复杂度为
O(n * m * m)
,其中n
是我们拥有的元素数量, m
是元素可以容纳的最大可能值。
The space complexity is O(n * m)
as this is dominated by the size of the dp
and prev
tables.空间复杂度为
O(n * m)
,因为这主要dp
和prev
表的大小。
We can use a greedy algorithm: proceed through the array in order, setting each element to the least value that is greater than the previous element and has digits with the appropriate sum.我们可以使用贪心算法:按顺序遍历数组,将每个元素设置为大于前一个元素的最小值,并具有适当总和的数字。 (We can just iterate over the possible values and check the sums of their digits.) There's no need to consider any greater value than that, because increasing a given element will never make it possible to decrease a later element.
(我们可以遍历所有可能的值并检查它们数字的总和。)没有必要考虑任何比这更大的值,因为增加给定元素永远不可能减少后面的元素。 So we don't need dynamic programming here.
所以我们这里不需要动态规划。
We can calculate the sum of the digits of an integer m in O(log m ) time, so the whole solution takes O( b log b ) time, where b is the upper bound (500 in your example).我们可以在 O(log m ) 时间内计算 integer m的数字总和,因此整个解决方案需要 O( b log b ) 时间,其中b是上限(在您的示例中为 500)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.