[英]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,因此我們可以限制算法搜索的死亡。
我想到的一種可能更快找到答案的方法是:
我認為這是一個貪婪的解決方案,但我不太確定時間復雜度。 另外我想知道這個問題有更好的解決方案嗎? 喜歡用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)
,因為這主要dp
和prev
表的大小。
我們可以使用貪心算法:按順序遍歷數組,將每個元素設置為大於前一個元素的最小值,並具有適當總和的數字。 (我們可以遍歷所有可能的值並檢查它們數字的總和。)沒有必要考慮任何比這更大的值,因為增加給定元素永遠不可能減少后面的元素。 所以我們這里不需要動態規划。
我們可以在 O(log m ) 時間內計算 integer m的數字總和,因此整個解決方案需要 O( b log b ) 時間,其中b是上限(在您的示例中為 500)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.