Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:

1
2
3
4
[
[7],
[2, 2, 3]
]
题意

给出一系列数字,和一个总和target,给出所有的排列使得和等于target,返回所有的序列。

答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
vector<vector<int>>res;
vector<int> ans;
int nums_len;

vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
if(candidates.empty()) return res;
nums_len = candidates.size();
dfs(0, 0, target, candidates);
return res;
}
void dfs(int index, int sum, int target,vector<int> candidates)
{
//一般DFS中需要有一个index,用来标记现在把那个节点遍历过了,所以为了不重复,前面先进行了排序。
if(sum==target) //死胡同
{
res.push_back(ans);
return;
}
else if(sum>target) //死胡同
{
return;
}
else
{
// 因为每个岔路口不止一种情况,而是candidates.size()情况,所以用 循环把每种情况都进行遍历。
for(int i=index;i<nums_len;i++) //岔道口
{
ans.push_back(candidates[i]);
dfs(i,sum+candidates[i],target,candidates);
ans.pop_back();
}
}
}
};

深度优先搜索DFS

深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法。

方法是:

​ 先对问题进行分析,得到岔路口和死胡同,再定义一个栈,以深度为关键词访问这些岔道口和死胡同,并将他们入栈,而当离开这些岔道口和死胡同时,再将他们入栈。

​ 当用递归进行实现时,递归中的递归式就是岔道口,而递归边界就是死胡同。

​ 使用递归时,系统会调用一个叫系统栈的东西来存放递归中每一层的状态,因此用递归实现的dfs的本质还是栈。

回溯法

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。可以认为回溯算法一个”通用解题法“,这是由他试探性的行为决定的,就好比求一个最优解,我可能没有很好的概念知道怎么做会更快的求出这个最优解,但是我可以尝试所有的方法,先试探性的尝试每一个组合,看看到底通不通,如果不通,则折回去,由最近的一个节点继续向前尝试其他的组合,如此反复。这样所有解都出来了,在做一下比较,能求不出最优解吗?

基本概念与定义

约束函数:约束函数是根据题意定出的。通过描述合法解的一般特征用于去除不合法的解,从而避免继续搜索出这个不合法解的剩余部分。因此,约束函数是对于任何状态空间树上的节点都有效、等价的。

状态空间树:刚刚已经提到,状态空间树是一个对所有解的图形描述。树上的每个子节点的解都只有一个部分与父节点不同。

扩展节点、活结点、死结点:所谓扩展节点,就是当前正在求出它的子节点的节点,在DFS中,只允许有一个扩展节点。活结点就是通过与约束函数的对照,节点本身和其父节点均满足约束函数要求的节点;死结点反之。由此很容易知道死结点是不必求出其子节点的(没有意义)。