一种直接的方法是递归过程,该过程在每次调用时执行以下操作。该过程的输入是已选择的对的列表和所有对的列表。
- 计算输入列表中尚未涵盖的最小数字。对于第一次调用,当然将为0,因为尚未选择任何对。
- 如果所有数字均已覆盖,则您的组合正确,将其打印出来并返回上一步。否则,发现的最小数量就是我们要追求的目标。
- 搜索对,寻找覆盖目标号码的方法。如果没有,则只需返回上一级递归。
- 如果有一种方法可以覆盖目标编号,请选择第一种方法,然后再次递归调用整个过程,将刚刚选择的对添加到所选对的列表中。
- 当返回时,寻找用一对覆盖目标号码而不重叠先前选择的对的另一种方法。如果找到一个,请选择它,然后再次递归调用下一个过程。
- 继续执行步骤4和5,直到没有其他方法可以覆盖目标号码为止。浏览整个配对对。如果没有其他正确的选择,请返回上一级递归。
可视化此算法的方法是使用一棵树,该树的路径是不重叠对的序列。树的第一级包含所有包含0的对。对于上面的示例,树为
根
|
----------------
| | |
(0,1)(0,2)(0,3)
| | |
(2,3)(1,3)(1,2)
在此示例中,穿过树的所有路径实际上都给出了正确的集合,但是例如,如果我们省略对(1,2),则最右边的路径将只有一个节点,并且对应于步骤3中的搜索失败。
可以针对枚举特定类型的所有对象的许多类似问题开发这种类型的搜索算法。
有人建议说,OP可能意味着所有对都在输入中,而不仅仅是问题中提到的一组。在那种情况下,该算法要容易得多,因为不再需要检查允许的配对。甚至没有必要生成所有对的集合。以下伪代码将执行OP要求的操作。这里是输入数字,“ list”开始是一个空列表,“ covered”是长度为n的数组,初始化为0。可以提高效率,但这不是我的近期目标。ññ
sub cover {
i = 0;
while ( (i < n) && (covered[i] == 1 )) {
i++;
}
if ( i == n ) { print list; return;}
covered[i] = 1;
for ( j = 0; j < n; j++ ) {
if ( covered[j] == 0 ) {
covered[j] = 1;
push list, [i,j];
cover();
pop list;
covered[j] = 0;
}
}
covered[i] = 0;
}