固定元组和可变元组是基于解的表达形式,固定元组使用0、1表示某元素是否存在,可变元组则直接给出可行解包含的元素。使用回溯算法解决子集求和问题的示例如下:
public class SumOfSub {
int[] x;// 固定元组
float[] w;//权值
float m;//和
public SumOfSub(int n, float[] w, float m) {
x = new int[n + 1];
this.w = w;
this.m = m;
}
public void solve(float s, int k, float r) {
x[k] = 1;
if (s + w[k] == m) {
for (int j=1;j<=k;j++) System.out.print(x[j]+" ");
System.out.println();
} else if (s + w[k] + w[k + 1] <= m) solve(s + w[k], k + 1, r - w[k]);
if (s + r - w[k] >= m&& s + w[k + 1] <= m ) {
x[k] = 0;
solve(s, k + 1, r - w[k]);
}
}
public static void main(String[] args) {
float[] w = { 0, 5, 10, 12, 13, 15, 18 };
SumOfSub a = new SumOfSub(6, w, 30);
a.solve(0, 1, 73);
}
}
w数组默认为严格递增。注意到一开始s+r=0+r>m,而每层递归调用时皆满足s + w[k + 1] <= m ;每次调用时必定有s+r>=m,因为如果没有进入过右子树,则s+r不变大于m,如果进入过右子树,则由生成右子树的判定s + r - w[k] >= m知s+r>=m。所以由
{
s
+
w
[
k
+
1
]
<
=
m
s
+
r
>
=
m
s
!
=
m
\begin{cases} \ s + w[k + 1] <= m\\ \ s+r>=m\\ \ s!=m \end{cases}
⎩
⎨
⎧ s+w[k+1]<=m s+r>=m s!=m
得
{
s
+
w
[
k
]
<
m
s
+
r
>
=
m
r
!
=
0
\begin{cases} \ s + w[k] < m\\ \ s+r>=m\\ \ r!=0 \end{cases}
⎩
⎨
⎧ s+w[k]<m s+r>=m r!=0
进而有
{
r
!
=
w
[
k
]
r
⩾
w
[
k
]
+
w
[
k
+
1
]
k
+
1
⩽
n
\begin{cases} \ r!=w[k] \\ \ r \geqslant w[k]+w[k+1] \\ \ k+1⩽ n \end{cases}
⎩
⎨
⎧ r!=w[k] r⩾w[k]+w[k+1] k+1⩽n
保证了数组不会越界。