问题描述
我在
竞争性程序员手册也可以做到这一点,但我正努力理解其背后的逻辑。
它指出:
像子集一样,可以使用递归来生成排列。以下 函数搜索遍历集合{0,1,...,n¡1}的排列。的 函数构建包含该置换的向量置换,并且 当调用不带参数的函数时,搜索开始。
void search() {
if (permutation.size() == n) {
// process permutation
} else {
for (int i = 0; i < n; i++) {
if (chosen[i]) continue;
chosen[i] = true;
permutation.push_back(i);
search();
chosen[i] = false;
permutation.pop_back();
}
}
}
我似乎无法理解正确的直觉和所使用的概念。
有人可以向我解释代码在做什么以及其背后的逻辑是什么吗?
解决方法
您可以像这样拆分代码:
void search() {
if (permutation.size() == n) {
// we have a valid permutation here
// process permutation
} else {
// The permutation is 'under construction'.
// The first k elements are fixed,// n - k are still missing.
// So we must choose the next number:
for (int i = 0; i < n; i++) {
// some of them are already chosen earlier
if (chosen[i]) continue;
// if i is still free:
// signal to nested calls that i is occupied
chosen[i] = true;
// add it to the permutation
permutation.push_back(i);
// At the start of this nested call,// the permutation will have the first (k + 1)
// numbers fixed.
search();
// Now we UNDO what we did before the recursive call
// and the permutation state becomes the same as when
// we entered this call.
// This allows us to proceed to the next iteration
// of the for loop.
chosen[i] = false;
permutation.pop_back();
}
}
}
直觉可能是search()
是“以所有可能的方式完成当前部分构造的置换并处理所有置换”。
如果它已经完成,我们只需要处理一个可能的排列。 如果没有,我们可以首先以各种可能的方式选择第一个数字 ,然后针对每种方式,递归地完成排列。
,我会尝试给你一些直觉。主要思想是 回溯 。您基本上会构建一个解决方案,直到遇到死胡同。当您遇到死胡同时,请回到最后一个位置,在这里您可以执行与上次不同的操作。让我来看一下我为region
绘制的模拟。
首先,你什么都没有。先走n = 3
,然后走1
再走2
。您现在无处可去,即 Dead End 。您将打印当前的3
排列,您现在要做什么?回到123
,因为您知道这次可以走1
可以走另一条路。那么,您这次以同样的方式得到什么? 3
。 使用1还能执行其他操作吗? 否 。现在回到一无所有,重新开始同样的事情,现在使用132
。您现在明白了吧?
对于代码中发生的相同情况:
2