簡體   English   中英

在對每個元素的數字求和后重建一個整數數組

[英]Rebuild an array of integers after summing the digits of each element

我們有一個長度為n (1 < n < 500)的嚴格遞增數組。 我們將每個元素的數字相加以創建一個新數組,每個元素的值在 1 到 500 之間。任務是從新數組重建舊數組。 由於可能有多個答案,我們希望答案具有最后一個元素的最小值。 例子:

3 11 23 37 45 123 =>3 2 5 10 9 6

現在從第二個數組,我們可以用許多不同的方式重建原始數組,例如:

12 20 23 37 54 60

從所有可能的組合中,我們需要最小化最后一個元素。

到目前為止我的想法:

蠻力方法是找到所有可能的排列來創建每個數字,然后創建第二個數組的所有數字的所有可能組合,並找到最后一個元素最少的組合。 很明顯,這不是一個好的選擇。

使用算法(使用指數時間),我們可以創建所有可能的數字排列,總和為第二個數字 arrays。請注意,我們知道原始元素少於 500,因此我們可以限制算法搜索的死亡。

我想到的一種可能更快找到答案的方法是:

  1. 從新的 arrays 中的最后一個元素開始,找到所有可能的數字,它們的數字總和是這個元素的結果。
  2. 然后嘗試在最后一步中為該元素使用最小量。
  3. 現在嘗試對倒數第二個元素執行相同的操作。 如果為倒數第二個元素找到的最小排列值大於為最后一個元素找到的最小排列值,則回溯到最后一個元素並嘗試更大的排列。
  4. 這樣做直到到達第一個元素。

我認為這是一個貪婪的解決方案,但我不太確定時間復雜度。 另外我想知道這個問題有更好的解決方案嗎? 喜歡用dp嗎?

為簡單起見,讓我們以序列1為基礎,輸入序列稱為x

我們還將使用實用程序 function,它返回給定數字的數字總和:

int sum(int x) {
  int result = 0;
  while (x > 0) {
    result += x % 10;
    x /= 10;
  }
  return result;
}

讓我們假設我們在索引idx並嘗試在那里設置一些稱為value的數字(假設value的數字總和為x[idx] )。 如果我們這樣做,那么對於序列中的前一個數字我們能說些什么呢? 它應該嚴格小於value

所以我們已經有一個 state 用於潛在的dp方法 - [idx, value] ,其中idx是我們當前所在的索引, value表示我們試圖在此索引上設置的值。

如果dp表包含boolean值,如果我們為序列中的第一個數字找到了合適的數字,我們就會知道我們已經找到了答案。 因此,如果有一條路徑dp表的最后一行開始到第0行結束,那么我們就會知道我們找到了答案,然后我們可以簡單地恢復它。

我們的循環 function 將是這樣的:

f(idx, value) = OR {dp[idx - 1][value'], where sumOfDigits(value) = x[idx] and value' < value}
f(0, *) = true

此外,為了恢復答案,我們需要跟蹤路徑。 一旦我們將任何dp[idx][value]單元格設置為 true,那么我們就可以保護我們想要在前一個表行中跳轉到的value'

現在讓我們編寫代碼。 我希望代碼是不言自明的:

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;
        }
      }
    }
  }
}

prev表只保留關於哪個是最小值value'信息,我們可以在結果序列中將其用作我們的idx之前的信息。

現在,為了恢復序列,我們可以從最后一個元素開始。 我們希望它是最小的,所以我們可以找到第一個dp[n][value] = true 一旦我們有了這樣的元素,我們就可以使用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(' ');
}

如果我們將其應用於輸入序列:

3 2 5 10 9 6

我們得到

3 11 14 19 27 33 

時間復雜度為O(n * m * m) ,其中n是我們擁有的元素數量, m是元素可以容納的最大可能值。

空間復雜度為O(n * m) ,因為這主要dpprev表的大小。

我們可以使用貪心算法:按順序遍歷數組,將每個元素設置為大於前一個元素的最小值,並具有適當總和的數字。 (我們可以遍歷所有可能的值並檢查它們數字的總和。)沒有必要考慮任何比這更大的值,因為增加給定元素永遠不可能減少后面的元素。 所以我們這里不需要動態規划。

我們可以在 O(log m ) 時間內計算 integer m的數字總和,因此整個解決方案需要 O( b log b ) 時間,其中b是上限(在您的示例中為 500)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM