回溯法例題之子集樹:陣列定和問題
阿新 • • 發佈:2019-01-28
問題描述:給定一個數組a和一個整數sum,在陣列中取若干個數使得他們的和等於sum
問題分析:陣列中的元素要麼選擇,要麼不選,且無序,每一種選擇都可以看做是解空間的一個元素
X[i] = 0或1(0<i<n, n為陣列a的長度)
如果我們假設當前已經處理了前t個元素(這t個元素或者選,或者不選),他們的和為c,那麼在處理第t+1個元素時,要滿足c + a[t+1] < sum,否則,該元素選擇進去之後由於所有的陣列元素都是正數,該和必定大於sum了,故該約束條件可以用來減枝,並且,我們注意到,如果c + (除前t個元素剩下所有元素的和) < sum 表示即使剩下的陣列元素都選中也不可能達到sum,那麼,這條枝丫也可以減去,因為不管怎麼選也不可能產生解,我們可以將陣列從大到小排序,這樣就可以勁量多減枝了(留給讀者自己思考)
#include <iostream> using namespace std; int m_count = 0; void output(int a[], int x[], int m) { for (int i = 0; i <= m; i++) { if (x[i] == 1) { cout << a[i] << " "; } } cout << endl; } void BackTrack(int t, int a[], int x[], int s[], int n, int sum, int& c) { ++m_count; if (t == n) { return; } if (c + a[t] < sum && s[t] + c >= sum) { x[t] = 1; c += a[t]; BackTrack(t+1, a, x, s, n, sum, c); c -= a[t]; x[t] = 0; } else if (c + a[t] == sum) { x[t] = 1; output(a, x, t); x[t] = 0; } BackTrack(t+1, a, x, s, n, sum, c); } int main() { int a[10] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; //這裡我就預設排序過了 int x[10] = {0}; int s[10] = {0}; int sum = 15; int c = 0; s[9] = 1; for (int i = 8; i >= 0; i--) { s[i] = s[i+1] + a[i]; } BackTrack(0, a, x, s, 10, sum, c); cout << "執行了" << m_count << "次" << endl; return 0; }