搜索问题,扩展节点的时候会iterate一个link集合,一般就是一个for 循环,但有的时候,这个集合并不能直接得到,而是另一个搜索问题,比如n个数分k组问题的一种解法是:问题分成k个步骤,每个步骤确定一个组,具体一个组可分的方案的candidate集合就不是直接可以用for来iterate的,而是另一个搜索问题——子集问题。这就引出一个重点,iterate不光可以额用for,也可以用搜索,搜索本来就是在枚举,只不过枚举出一个解不是print输出,而是用来作为外层搜索的一个扩展节点,另外一点,搜索的结果体现在哪?side effect,搜索一般是通过side effect体现状态、结果的。
更新:
嵌套搜索的思路只是一个看问题的角度,把维度拆开来一个一个看。也可以有另一种更general的角度:二元(多元)搜索。每一次递归就是一次状态转移,一般的,这个状态是一个变量,其他的变量都是伴随状态;多元搜索的意思是状态是一个组合(x, y),类似二维平面上的搜索,看下面终极版搜索。
def group(A, k):
groups, n, used = [[] for _ in xrange(k)], len(A), set()
def grouping(t, start):
if t == k:
if len(used) == n: print groups
return
if start == n: return
if start in used: grouping(t, start + 1)
else:
groups[t].append(A[start])
used.add(start)
def select(i, startIndex):
grouping(t + 1, start + 1)
for j in xrange(startIndex, n):
if j in used: continue
used.add(j)
groups[t].append(A[j])
select(i + 1, j + 1)
used.remove(j)
groups[t].pop()
select(start + 1, start + 1)
groups[t].pop()
used.remove(start)
grouping(0, 0)
第二个版本
def group(A, k):
groups, n, used = [[] for _ in xrange(k)], len(A), set()
def grouping(t, start):
if t == k:
if len(used) == n: print groups
return
if start == n: return
if start in used: grouping(t, start + 1)
else:
def select(i, startIndex):
grouping(t + 1, start + 1)
for j in xrange(startIndex, n):
if j in used: continue
used.add(j)
groups[t].append(A[j])
select(i + 1, j + 1)
used.remove(j)
groups[t].pop()
select(start, start)
grouping(0, 0)
递归问题法:
def group(A, k):
if k == 1:return [[A[:]]]
if k == len(A): return [map(lambda x: [x], A)]
last = A.pop()
groupings = group(A, k - 1)
for grouping in groupings: grouping.append([last])
for grouping in group(A, k):
for g in grouping:
g.append(last)
groupings.append(deepcopy(grouping))
g.pop()
A.append(last)
return groupings
终极版本:
def group(A, k):
groups, used = [[] for _ in xrange(k)], set()
def grouping(g, start):
if g == k:
if len(used) == len(A): print groups
else:
if len(groups[g]) > 0: grouping(g + 1, g + 1)
for i in xrange(start, len(A)):
if i in used: continue
groups[g].append(A[i])
used.add(i)
grouping(g, i + 1)
groups[g].pop()
used.remove(i)
grouping(0, 0)
还有一个例子:leetcode 282 expression add operators,给一串数字加操作符凑一个给定的值。问题的划分:每一步取若干数字作为一个数,参与计算,下一步取剩下的,取完为止。每一步按取几个数字作为数又分多种情况,同时确定了一个数之后按进行什么运算分又有4种情况。这里扩展节点是一个双重循环,先枚举取几个数字作为操作数,再枚举4种运算。
vector<string> addOperators(string num, int target) {
vector<string> ans;
function<void(int, long long, long long, string)> dfs = [&](int i, long long eval, long long pre, string s)->void {
if (i == num.size()) {
if (target == eval) ans.push_back(s);
}
else {
for (int j = i; j < num.size(); ++j) {
if (num[i] == '0' && j > i) break;
string cur = num.substr(i, j - i + 1);
long long value = stol(cur);
if (i == 0) {
dfs(j + 1, value, value, cur);
}
else {
dfs(j + 1, eval + value, value, s + "+" + cur);
dfs(j + 1, eval - value, -value, s + "-" + cur);
dfs(j + 1, eval - pre + value * pre, value * pre, s + "*" + cur);
}
}
}
};
dfs(0, 0, 0, "");
return ans;
}