給定乙個整數陣列 a,我們希望將 A 陣列中的每個元素移動到 b 或 c 陣列。 (b 和 c 陣列在開頭都是空的)。
當且僅當我們完成了這樣的移動時,才返回 true,因此 B 陣列的平均值和 C 陣列的平均值相等,並且 B 陣列和 C 陣列都不是空的。
示例: 輸入:
輸出:true
解釋:我們可以將陣列分為 [1,4,5,8] 和 [2,3,6,7],它們的平均值都是 45。
注意:a-array 的長度範圍為 [1, 30]。
a[i] 的資料範圍為 [0, 10000]。
回顧性實際分離列表 b 和 c 的平均值等於列表 a 的平均值,這是這個問題的起點。 證據如下:
設 b 的長度為 k,a 的長度為 n。 是的sum(b)/k = sum(c)/(n-k)
然後:
sum(b) *n - k) = sum(c) *ksum(b) *n = (sum(b) +sum(c)) ksum(b) / k = (sum(b) +sum(c)) / nsum(b) / k = sum(a) / n
因此,我們可以列舉所有 a 的大小 i,因此 b 的大小為 n - i,其中 n 是陣列 a 的大小。
由於列表 b 和 c 的平均值等於列表 a 的平均值。因此,我們可以提前計算出 a 的平均值 **g,那麼 a 的總和實際上是 i * g,我們使用回溯來求 i * g 之和的組合,我們可以返回 true,否則返回 false。
值得注意的是,我們只需要列舉 1 到 n2 範圍內的 i,就可以達到修剪的效果。
核心**:
def splitarraysame**erage(self, a: list[int]) bool: n = len(a) *g = sum(a) / n for i in range(1, n // 2 + 1): for combination in combinations(a, i): if abs(sum(combination) -g * i) <1e-6: return true return false
上圖**由於回溯與 sum 巢狀,因此時間複雜度為回溯的時間複雜度 * 和的時間複雜度,因此在最壞的情況下,總時間複雜度為 $n * 2 n$。 替換問題的 n 範圍是 30,一般這種複雜度只能解決 20 以下的問題,因此需要考慮優化。
我們可以直接計算它們,而不是將所有組合相加all 和,則該演算法的時間複雜度為 $2 n$。
核心**:
def splitarraysame**erage(self, a: list[int]) bool: n = len(a) *g = sum(a) / n for i in range(1, n // 2 + 1): for s in combinationsum(a, i): if abs(s - g * i) <1e-6: return true return false
不幸的是,這仍然不足以通過所有測試用例。
接下來,我們可以通過進一步修剪的方式達到AC的目的。 好多回溯這些問題都是基於修剪的。 修剪是回溯問題的核心測試點。
這個招數是雙向搜尋,雙向搜尋可以將指數數從 $o(2 n)$ 減少到 $o(2 (n 2))$ 進入問題,使指數變為 30 2 = 15,並且可以通過。
具體來說,我們可以將組合總和的一半變成陣列(你可以稱之為 a1),然後我們可以將組合總和的一半變成陣列(你可能會認為呼叫 a2),那麼 a1 和 a2 的總和不是 **g * i 嗎? 為簡單起見,我們可以將 a1 設定為陣列 a 的前半部分,將 a2 設定為陣列的後半部分。
同時,為了避免這種新增,我們可以對問題進行改造。 也就是說,從陣列 a 的所有數字中減去 **g,這樣問題就會轉化為找到總和 0 的組合,即,您可以找到總和 和 **g * i 的組合。
雙端搜尋語言支援:python3
class solution(object): def splitarraysame**erage(self, a): from fractions import fraction n = len(a) total = sum(a) a = [a - fraction(total, n) for a in a] 轉換後,如果 n == 1,則免於計算總和:返回 false s1 = set() 範圍 (n 2) 中 i 的所有可能的 b 和的集合: 在前乙個選擇的基礎上選擇 a[i] 的新集合是僅選擇a[i] s1 是不選擇 a[i] |是乙個集合並運算 s1 = |s1 |如果 s1 中的 0: 返回 true s2 = set() 範圍(n 2, n) 中 i 的所有可能 c 和的集合: s2 = |s2 |如果 S2 中的 0:如果 S1 和 S2 都沒有 0 的總和組合,則返回 true。 然後我們需要分別從 s1 和 s2 中找到 a 和 b,看看總和是否可以達到 0如果是這樣,描述也將滿足問題的含義 為了避免 b 或 c 為空,我們新增這樣的判斷:(ha, -ha) != (sleft, sright) sleft = sum(a[i] for i in range(n//2)) sright = sum(a[i] for i in range(n//2, n)) return any(-ha in s2 and (ha, -ha) != (sleft, sright) for ha in s1)
複雜性分析
設 n 為陣列的長度。
時間複雜度:$o(2 (n 2))$ 空間複雜度:$o(2 (n 2))$