忒休斯的高招,和现代计算机中求解很多问题的算法有异曲同工之妙,很多应用问题的解,在形式上都可以看做若干元素按特定次序构成的一个序列
通常,其搜索空间的规模与全排列大体相当,为n! = O(n**n),因此若使用蛮力策略,逐一生成可能的候选解并检查其是否合理,则必然无法将允许时间控制在多项式的范围以内。
剪枝出场。
根据候选解的某些局部特征,以候选解子集为单位批量的排除。
这种从长度上主句向目的解靠近的尝试,称为试探
作为解的局部特征,特征前缀在试探的过程中一旦被发现与目标不符合,则收缩到此前一步的长度,然后继续试探下一可能的组合,称为回溯
看个例题
LeetCode 18题 四数之和
大致介绍:
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
此处剪枝大致有四个条件:
- 如果剩余可选的数字数量少于 nn,则剪掉(递归返回);
- 每层递归的 for 循环中,从第二轮循环开始,如果数组中当前数字和前一个相同,则剪掉(进行下一次循环,这条的任务就是去重);
- 如果 当前数字 + 已确定数字的和 + (n - 1) * 排序后数组中当前数字的下一个数字 > target当前数字+已确定数字的和+(n−1)∗排序后数组中当前数字的下一个数字>target,则说明后面的数无论怎么选,加起来都一定大于 targettarget,所以剪掉(递归返回);
- 如果 当前数字 + 已确定数字的和 + (n - 1) * 排序后数组最后一个数字 < target当前数字+已确定数字的和+(n−1)∗排序后数组最后一个数字<target,则说明后面的数无论怎么选,加起来都一定小于 targettarget,所以剪掉(进行下一次循环)。
代码如下
class Solution {
private:
vector<vector<int>> ans;
vector<int> myNums, subans;
int tar, numSize;
void DFS(int low, int sum) {
if (sum == tar && subans.size() == 4) {
ans.emplace_back(subans);
return;
}
for (int i = low; i < numSize; ++i) {
if (numSize - i < int(4 - subans.size())) { //剪枝
return;
}
if (i > low && myNums[i] == myNums[i - 1]) { //去重
continue;
}
if (i < numSize - 1 && sum + myNums[i] + int(3 - subans.size()) * myNums[i + 1] > tar) { //剪枝
return;
}
if (i < numSize - 1 && sum + myNums[i] + int(3 - subans.size()) * myNums[numSize - 1] < tar) { //剪枝
continue;
}
subans.emplace_back(myNums[i]);
DFS(i + 1, myNums[i] + sum);
subans.pop_back();
}
return;
}
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
myNums = nums;
tar = target;
numSize = nums.size();
if (numSize < 4) {
return ans;
}
DFS(0, 0);
return ans;
}
};
是不是看的很懵?那就对了,下面我来简单的模拟一下全排列的过程,把题目中数组中的元素替换成a,b,c,d,消除剪枝部分,来看看运行过程。
定义全局变量
// ans:全排列数组
vector<vector<int>> ans;
// myNums:目标数组,subans:全排列数组内数组
vector<int> myNums, subans;
// tar:目标值,numsize:数组值的个数
int tar, numSize;
下面到了最重要的入栈环节!
1 DFS(0, 0);
2 i = 0, subans.push_back(a)
3 DFS(1, 0 + a)
4 i = 2, subans.push_back(b)
5 DFS(3, 0 + a + b)
6 i = 3, subans.push_back(c)
7 DFS(4, 0 + a + b + c)
8 subans.size() == 4, ans.push_back(subans)
9 i = 3, subans.pop_back(c), subans = {a, b}
10 i < numsize
11 subans.pop_back(b)
12 i = 2, subans.push_back(c)
...
最后会形成
a+b+c