1. 程式人生 > >回溯法例題之子集樹:陣列定和問題

回溯法例題之子集樹:陣列定和問題

問題描述:給定一個數組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;
}