递归与回溯5:剪枝优化

我们说过,回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的,主要就是去掉那些不必要的递归,从而提高执行效率。例如假如有五个男孩子都和一个女生说要厮守终生。她会和这五个人都先过一辈子再确定谁会真正做到吗?当然不会。而是会先思考一下,有一个可能导出勾勾搭搭,那她可以认为这个人只是一时兴起,剪掉!有一个可能太窝囊了,什么都不会,那她可以认为和他在一起会非常累,剪掉!有一个长得太对不起祖宗,可能会影响下一代,剪掉!最后就剩下两个,再进一步考虑!这就是剪枝!
在上一篇回溯的题目中,我们得遍历过程是这样的:

for (int i = startIndex; i <= n; i++) {
    path.addLast(i);
    dfs(n, k, i + 1, path, res);
}

这个遍历的范围是可以剪枝优化的,怎么优化呢?
来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了。

递归与回溯5:剪枝优化_第1张图片

图中每一个节点(图中为矩形),就代表本层的一个for循环,那么每一层的for循环从第二个数开始遍 历的话,都没有意义,都是无效遍历。所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。

根据一个材料的说明,对于任何一次循环,已经选择的元素个数就是path.size(),还需要的元素个数为: k - path.size(),然后只要选择从开始到n - (k - path.size()) + 1就行了,但是我没太想明白为什么用这个。后面想清楚了再补充。

    public static void dfs(int n, int k, int startIndex, Deque path, List> res) {
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i <= n-(k-path.size())+1; i++) {
            path.addLast(i);
            dfs(n, k, i + 1, path, res);
            path.removeLast();
        }
    }

回溯的剪枝优化根据题目情况会有很多变化,这个只能具体问题具体分析了 。

你可能感兴趣的:(算法,剪枝,算法,机器学习)