【学习笔记】搜索与枚举
1.启发式搜索(A*)
对于一个搜索状态 \(x\),令 \(h(x)\) 为它到实际答案的步数,我们给它一个估价 \(f(x)=g(x)+H(x)\)。其中 \(g(x)\) 为搜到当前状态的步数,\(H(x)\) 为这个状态到目标状态的预估步数。
如果预估函数写得好能够在很短的时间内得到解。但是要保证 \(H(x)\le h(x)\)。
流程就是搞成一个优先队列然后每次把 \(f(x)\) 最小的点拿出来扩展。
第一个扩展出来的一定是最优解。
证明:
令现在出来的解是 \(x\),它是由 \(z\) 扩展出来的,那么 \(H(z)=h(z)\)。因为只剩一条路只要你的预估函数有一点智商都会走这个,所以此时的 \(f(z)\) 是真实值。
也就是说走到 \(x\) 之前的 \(f(z)\) 全部是真实值,所以我们出来的第一个解就是最优解。
2.迭代加深
不考虑空间的情况下不如广搜。
考虑广搜的实质是空间换时间,dfs 的劣势在可能答案很近但是直接跑开了。
所以我们限制 dfs 层数 \(d\),传东西的时候传 \(d\) 下去,超出限制直接返回。搜到解退出,不然 \(d+1\) 再搜。
可以发现会有很多重复的搜索,但是理性考虑一下,我们每一次拓展至少要拓宽两倍,那么我们重复多搜一次也没有太大的关系。
相比于 bfs 优点在于空间需求小,而且 不需要保存状态。
3.常见的搜索枚举
- 子集枚举
普通子集枚举是 \(2^n\) 的,然后枚举自己内子集(就是对于每个枚举出来的子集 \(S\) 枚举它的子集)可以做到 \(3^n\)。不会证,但是大概不需要证。
for(int S=0;S<(1<<n);S++)for(int T=S;T;T&=(T-1))do sth;
不记得是不是这个了。