【算法設計與分析】將數字分解爲n個數字之和

【例】數字6可分解爲

6

5+1

4+2        4+1+1

3+3        3+2+1         3+1+1+1

2+2+2    2+2+1+1    2+1+1+1+1

1+1+1+1+1+1

思路:回溯算法,搜索所有情況,只保留符合條件的

遞歸終止條件:臨時數組求和等於n則加入結果集,同時結束遞歸

遞歸過程:循環遍歷1..n,將新數字加入臨時數組中進入下一層遞歸,出來後再將其移除

回溯的關鍵在於,添加和移除,保證所有可能性都被遍歷到,整體結構和棧類似

代碼

# 結果集(解空間),全局變量保存最終結果
result_set = []


def foo(n, result=None):
    """n爲要分解的數字的2倍,result爲臨時結果"""
    if result is None:
        result = []
    # 當n正好與result中元素求和相等時把result作爲一種解,添加到解空間result_set中
    if n == sum(result):
        global result_set
        # 對result進行排序是爲了方便判斷解空間result_set中是否包含result
        sorted_result = sorted(result[:], reverse=True)
        # 只有在結果集中不含result纔將其加入解空間,確保每個結果的唯一性
        if sorted_result not in result_set:
            result_set.append(sorted_result)
    else:
        # 從1..n把所有數字相加的所有情況列出來
        for i in range(1, n):
            # 爲了提高效率,
            # 對於(result + i) > (n - i)時直接回溯,因爲繼續執行result只會遞增,不再可能等於n
            if sum(result) + i > n - i:
                break
            result.append(i)
            foo(n - i, result)
            result.pop()


def combination_sum(n):
    foo(2 * n)
    return result_set


if __name__ == '__main__':
    res = sorted(combination_sum(6))
    print(res)
    # 打印結果:[[1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1], [2, 2, 1, 1], [2, 2, 2], [3, 1, 1, 1], [3, 2, 1], [3, 3], [4, 1, 1], [4, 2], [5, 1], [6]]
    # 爲了方便驗證結果,將結果格式化輸出
    res_size = len(res)
    for i in range(res_size - 1, -1, -1):
        if i + 1 < res_size and int(res[i][0]) != int(res[i + 1][0]):
            print('')
        res[i] = list(map(lambda x: str(x), res[i]))
        print('+'.join(res[i]), end=' ')
    # 打印結果:
    # 6
    # 5+1
    # 4+2 4+1+1
    # 3+3 3+2+1 3+1+1+1
    # 2+2+2 2+2+1+1 2+1+1+1+1
    # 1+1+1+1+1+1

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章