简体   繁体   中英

Rebuild an array of integers after summing the digits of each element

We have an strictly increasing array of length 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. 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

now from the second array, we can rebuild the original array in many different ways for instance:

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.

One way I thought of that might find the answer faster is to:

  1. start from the last element in the new arrays and find all possible numbers that their digit sum resulted this element.
  2. Then try to use the smallest amount in the last step for this element.
  3. Now try to do the same with the second to last element. If the minimum permutation value found for the second to last element is bigger than the one found for the last element, backtrack to the last element and try a larger permutation.
  4. Do this until you get to the first element.

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 ?

For simplicity, let's have our sequence 1 -based and the input sequence is called x .

We will also use an utility function, which returns the sum of the digits of a given number:

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] ). If we do so, then what could we say about the previous number in the sequence? It should be strictly less than 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.

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. 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.

Our recurrence function will be something like this:

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.

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.

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 . Once we have such element, we then use the prev table to track down the values up to the first one:

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.

The space complexity is O(n * m) as this is dominated by the size of the dp and prev tables.

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM