Search Techniques 搜索技术

本文深入探讨了搜索算法的核心原理,包括深度优先搜索、广度优先搜索及其变体迭代加深搜索。通过对经典问题如N皇后问题、骑士覆盖问题的剖析,阐述了不同搜索策略的应用场景与优劣比较。
Sample Problem: n Queens [Traditional]

Place n queens on an n x n chess board so that no queen is attacked by another queen.

n个女王放在nxn棋盘上,使不会有女王被另一个女王攻击。

Depth First Search (DFS)

The most obvious solution to code is to add queens recursively to the board one by one, trying all possible queen placements. It is easy to exploit the fact that there must be exactly one queen in each column: at each step in the recursion, just choose where in the current column to put the queen. 

代码最明显的解决方案是逐个递增地向棋盘添加棋王,尝试所有可能的女王位置。很容易利用这样一个事实,即每列中必须只有一个后置:在递归的每一步,只需选择当前列中放置后置的位置。


 1 search(col)
 2     if filled all columns
 3         print solution and exit 

 4   for each row
 5       if board(row, col) is not attacked
 6            place queen at (row, col)
 7            search(col+1)
 8            remove queen at (row, col)

Calling search(0) begins the search. This runs quickly, since there are relatively few choices at each step: once a few queens are on the board, the number of non-attacked squares goes down dramatically.

This is an example of depth first search, because the algorithm iterates down to the bottom of the search tree as quickly as possible: once k queens are placed on the board, boards with even more queens are examined before examining other possible boards with only k queens. This is okay but sometimes it is desirable to find the simplest solutions before trying more complex ones.

调用搜索(0)开始搜索。这种情况很快就会发生,因为每一步的选择相对较少:一旦棋盘上有几个皇后,非攻击方块的数量就会大幅下降。

这是一个例子深度优先搜索,因为该算法迭代尽快降低到搜索树的底部:一旦ķ皇后被放置在棋盘上,正在研究其他可能的板仅前研究了与更皇后板ķ皇后。这没关系,但有时需要在尝试更复杂的解决方案之前找到最简单的解决方案。

Depth first search checks each node in a search tree for some property. The search tree might look like this: 

深度优先搜索检查搜索树中的每个节点以查找某些属性。搜索树可能如下所示: 

The algorithm searches the tree by going down as far as possible and then backtracking when necessary, making a sort of outline of the tree as the nodes are visited. Pictorially, the tree is traversed in the following manner: 

算法通过尽可能向下搜索树,然后在必要时进行回溯,在访问节点时制作树的轮廓。以图形方式,树以下列方式遍历: 

Complexity

Suppose there are d decisions that must be made. (In this case d=n, the number of columns we must fill.) Suppose further that there are C choices for each decision. (In this case c=n also, since any of the rows could potentially be chosen.) Then the entire search will take time proportional to cd, i.e., an exponential amount of time. This scheme requires little space, though: since it only keeps track of as many decisions as there are to make, it requires only O(d) space.

假设必须做出d个决定。(在这种情况下,d=n,我们必须填充的列数。)进一步假设每个决策都有C种选项。(在这种情况下, c = n也是如此,因为可能会选择任何行。)然后整个搜索将花费与 c^d 成比例的时间,即指时间复杂度。然而,这种方案需要很小的空间:因为它只跟踪尽可能多的决策,它只需要O(d)空间。

Sample Problem: Knight Cover [Traditional]

Place as few knights as possible on an n x n chess board so that every square is attacked. A knight is not considered to attack the square on which it sits.

示例问题:骑士覆盖[传统]

nxn棋盘上放置尽可能少的骑士,以便每个方格都受到攻击。骑士不被认为攻击它所在的方块。

Breadth First Search (BFS)

In this case, it is desirable to try all the solutions with only k knights before moving on to those with k+1 knights. This is called breadth first search. The usual way to implement breadth first search is to use a queue of states: 

广度优先搜索(BFS)

在这种情况下,希望在移动到具有k + 1个骑士的那些之前尝试仅具有k个骑士的所有解决方案这称为广度优先搜索实现广度优先搜索的常用方法是使用状态队列: 

 1 process(state)
 2     for each possible next state from this one
 3         enqueue next state 

 4 search()
 5     enqueue initial state
 6     while !empty(queue)
 7         state = get state from queue
 8         process(state)

This is called breadth first search because it searches an entire row (the breadth) of the search tree before moving on to the next row. For the search tree used previously, breadth first search visits the nodes in this order: 

这称为广度优先搜索,因为它在搜索树的整行(宽度)之前搜索到下一行。对于先前使用的搜索树,广度优先搜索按以下顺序访问节点:

It first visits the top node, then all the nodes at level 1, then all at level 2, and so on.

它首先访问顶级节点,然后访问级别1的所有节点,然后访问级别2的所有节点,依此类推

Complexity

Whereas depth first search required space proportional to the number of decisions (there were n columns to fill in the n queens problem, so it took O(n) space), breadth first search requires space exponential in the number of choices.

If there are c choices at each decision and k decisions have been made, then there are k possible boards that will be in the queue for the next round. This difference is quite significant given the space restrictions of some programming environments.

[Some details on why ck: Consider the nodes in the recursion tree. The zeroeth level has 1 nodes. The first level has c nodes. The second level has c2 nodes, etc. Thus, the total number of nodes on the k-th level is ck.]

深度优先搜索所需的空间与决策数量成比例(n列中n列填充 n皇后问题,因此需要O(n)空间),广度优先搜索需要选择数量的空间指数。

如果有Ç在每个决定和选择ķ 决定已经作出,则有Ç ķ 可能主板,这将是在队列中等待下一轮。考虑到某些编程环境的空间限制,这种差异非常显着。

[有关k原因的一些细节:考虑递归树中的节点。零级别有1个节点。第一级有c个节点。第二级具有c 2个节点等。因此,第k级的节点总数是c k

Depth First with Iterative Deepening (ID)

An alternative to breadth first search is iterative deepening. Instead of a single breadth first search, run D depth first searches in succession, each search allowed to go one row deeper than the previous one. That is, the first search is allowed only to explore to row 1, the second to row 2, and so on. This ``simulates'' a breadth first search at a cost in time but a savings in space. 

深度优先与迭代深化(ID)

广度优先搜索的替代方案是迭代加深不是单个广度优先搜索,而是连续运行D深度优先搜索,每个搜索允许比前一个搜索更深一行。也就是说,第一次搜索仅允许探索到第1行,第二次搜索到第2行,依此类推。这种“模拟”'广泛的第一次搜索是成本的,但节省了空间。 

 1 truncated_dfsearch(hnextpos, depth)
 2     if board is covered
 3         print solution and exit 

 4     if depth == 0
 5         return 

 6     for i from nextpos to n*n
 7         put knight at i
 8         truncated_dfsearch(i+1, depth-1)
 9         remove knight at i 

10 dfid_search
11     for depth = 0 to max_depth
12        truncated_dfsearch(0, depth)

Complexity

The space complexity of iterative deepening is just the space complexity of depth first search: O(n). The time complexity, on the other hand, is more complex. Each truncated depth first search stopped at depth k takes ck time. Then if d is the maximum number of decisions, depth first iterative deepening takes c0 + c1 + c2 + ... + cd time.

If c = 2, then this sum is cd+1 - 1, about twice the time that breadth first search would have taken. When c is more than two (i.e., when there are many choices for each decision), the sum is even less: iterative deepening cannot take more than twice the time that breadth first search would have taken, assuming there are always at least two choices for each decision.

迭代深化的空间复杂度只是深度优先搜索的空间复杂度:O(n)。另一方面,时间复杂性更复杂。在深度k处停止的每个截断深度优先搜索花费k时间。然后,如果d是最大决策数,则深度第一次迭代加深需要0 + c 1 + c 2 + ... + c d时间。

如果c = 2,则该总和为d + 1 - 1,大约是广度优先搜索所用时间的两倍。c超过两个时(即,当每个决策有很多选择时),总和甚至更少:迭代加深不能超过广度优先搜索所花费的时间的两倍,假设总是至少有两个选择对于每个决定。

Which to Use?

Once you've identified a problem as a search problem, it's important to choose the right type of search. Here are some things to think about.

一旦您将问题确定为搜索问题,选择正确的搜索类型就很重要。这里有一些需要考虑的事情。

In a Nutshell
SearchTimeSpaceWhen to use
DFSO(ck)O(k)Must search tree anyway, know the level the answers are on, or you aren't looking for the shallowest number.
BFSO(cd)O(cd)Know answers are very near top of tree, or want shallowest answer.
DFS+IDO(cd)O(d)Want to do BFS, don't have enough space, and can spare the time.
d is the depth of the answer 
k is the depth searched 
d <= k

Remember the ordering properties of each search. If the program needs to produce a list sorted shortest solution first (in terms of distance from the root node), use breadth first search or iterative deepening. For other orders, depth first search is the right strategy.

If there isn't enough time to search the entire tree, use the algorithm that is more likely to find the answer. If the answer is expected to be in one of the rows of nodes closest to the root, use breadth first search or iterative deepening. Conversely, if the answer is expected to be in the leaves, use the simpler depth first search.

Be sure to keep space constraints in mind. If memory is insufficient to maintain the queue for breadth first search but time is available, use iterative deepening.

记住每次搜索的排序属性。如果程序需要首先生成列表排序最短的解决方案(根据与根节点的距离),请使用广度优先搜索或迭代深化。对于其他订单,深度优先搜索是正确的策略。

如果没有足够的时间搜索整个树,请使用更有可能找到答案的算法。如果预期答案位于最接近根的节点行之一,请使用广度优先搜索或迭代深化。相反,如果答案预计在叶子中,请使用更简单的深度优先搜索。

务必记住空间限制。如果内存不足以维持队列以进行广度优先搜索但时间可用,请使用迭代深化。

Sample Problems
Superprime Rib [USACO 1994 Final Round, adapted]

A number is called superprime if it is prime and every number obtained by chopping some number of digits from the right side of the decimal expansion is prime. For example, 233 is a superprime, because 233, 23, and 2 are all prime. Print a list of all the superprime numbers of length n, for n <= 9. The number 1 is not a prime.

For this problem, use depth first search, since all the answers are going to be at the nth level (the bottom level) of the search.

示例问题
Superprime Rib [USACO 1994决赛,改编]

如果一个数字是素数,则称为superprime,并且通过从十进制扩展的右侧截取一些数字而得到的每个数字都是素数。例如,233是超级抵押贷款,因为233,23和2都是素数。打印长度的所有超素号码的列表Ñ,对N <= 9数字1不是素数。

对于此问题,请使用深度优先搜索,因为所有答案都将位于搜索的第n级(底层)。


Betsy's Tour [USACO 1995 Qualifying Round]

A square township has been partitioned into 2 square plots. The Farm is located in the upper left plot and the Market is located in the lower left plot. Betsy takes a tour of the township going from Farm to Market by walking through every plot exactly once. Write a program that will count how many unique tours Betsy can take in going from Farm to Market for any value of n <= 6.

Since the number of solutions is required, the entire tree must be searched, even if one solution is found quickly. So it doesn't matter from a time perspective whether DFS or BFS is used. Since DFS takes less space, it is the search of choice for this problem.

Betsy的巡回赛[USACO 1995资格赛]

一个方形的乡镇被划分为2 平方的土地。农场位于左上方地块,市场位于左下方地块。Betsy参观了从农场到市场的小镇,只需走过一次。编写一个程序,计算Betsy从农场到市场可以采取多少独特的旅行,因为任何n <= 6的值 

由于需要解决方案的数量,因此即使快速找到一个解决方案,也必须搜索整个树。因此,从时间角度来看,是否使用DFS或BFS无关紧要。由于DFS占用的空间较少,因此它是此问题的首选搜索。


Udder Travel [USACO 1995 Final Round; Piele]

The Udder Travel cow transport company is based at farm A and owns one cow truck which it uses to pick up and deliver cows between seven farms A, B, C, D, E, F, and G. The (commutative) distances between farms are given by an array. Every morning, Udder Travel has to decide, given a set of cow moving orders, the order in which to pick up and deliver cows to minimize the total distance traveled. Here are the rules:

  • The truck always starts from the headquarters at farm A and must return there when the day's deliveries are done.
  • The truck can only carry one cow at a time.
  • The orders are given as pairs of letters denoting where a cow is to be picked up followed by where the cow is to be delivered.

Your job is to write a program that, given any set of orders, determines the shortest route that takes care of all the deliveries, while starting and ending at farm A.

Since all possibilities must be tried in order to ensure the best one is found, the entire tree must be searched, which takes the same amount of time whether using DFS or BFS. Since DFS uses much less space and is conceptually easier to implement, use that.

Udder Travel [USACO 1995决赛; Piele]

乳房旅行牛运输公司位于A农场,拥有一辆牛车,用于在七个农场A,B,C,D,E,F和G之间接收和运送奶牛。农场之间的(可交换的)距离由数组给出。每天早上,乳房旅行必须根据一系列奶牛移动订单决定采摘和运送奶牛的顺序,以最大限度地减少行进的总距离。以下是规则:

  • 卡车总是从A座的总部开始,必须在当天交货时返回。
  • 卡车一次只能携带一头牛。
  • 订单以成对字母的形式给出,表示要拾取奶牛的位置,然后是奶牛的送货地点。

您的工作是编写一个程序,在给定任何订单集的情况下,确定在A区开始和结束时负责所有交付的最短路径。

由于必须尝试所有可能性以确保找到最佳的可能性,因此必须搜索整个树,无论使用DFS还是BFS,都需要相同的时间。由于DFS使用的空间更少,并且在概念上更容易实现,因此使用它。


Desert Crossing [1992 IOI, adapted]

A group of desert nomads is working together to try to get one of their group across the desert. Each nomad can carry a certain number of quarts of water, and each nomad drinks a certain amount of water per day, but the nomads can carry differing amounts of water, and require different amounts of water. Given the carrying capacity and drinking requirements of each nomad, find the minimum number of nomads required to get at least one nomad across the desert.

All the nomads must survive, so every nomad that starts out must either turn back at some point, carrying enough water to get back to the start or must reach the other side of the desert. However, if a nomad has surplus water when it is time to turn back, the water can be given to their friends, if their friends can carry it.

Analysis: This problem actually is two recursive problems: one recursing on the set of nomads to use, the other on when the nomads turn back. Depth-first search with iterative deepening works well here to determine the nomads required, trying first if any one can make it across by themselves, then seeing if two work together to get across, etc.

沙漠过境[1992 IOI,改编]

一群沙漠游牧民正在共同努力让他们的一群人穿越沙漠。每个游牧民都可携带一定数量的夸脱水,每个游牧民每天饮用一定量的水,但游牧民可携带不同量的水,并需要不同量的水。考虑到每个游牧民的承载能力和饮酒需求,找到在沙漠中至少有一名游牧民所需的最少游牧民数。

所有的游牧民都必须活下来,所以每个开始的游牧民都必须在某个时刻回头,携带足够的水回到起点或者必须到达沙漠的另一边。然而,如果一个游牧民在需要回头的时候有多余的水,如果他们的朋友可以携带它,可以将水送给他们的朋友。

分析:这个问题实际上是两个递归问题:一个在游牧民族集合上使用,另一个在游牧民族回头时。具有迭代加深的深度优先搜索在这里很好地确定所需的游牧民族,如果任何人可以自己完成,则首先尝试,然后看看两个人是否一起工作以等。


Addition Chains

An addition chain is a sequence of integers such that the first number is 1, and every subsequent number is the sum of some two (not necessarily unique) numbers that appear in the list before it. For example, 1 2 3 5 is such a chain, as 2 is 1+1, 3 is 2+1, and 5 is 2+3. Find the minimum length chain that ends with a given number.

Analysis: Depth-first search with iterative deepening works well here, as DFS has a tendency to first try 1 2 3 4 5 ... n, which is really bad and the queue grows too large very quickly for BFS.

加法链

加法链是一个整数序列,使得第一个数字为1,每个后续数字是出现在它之前的列表中的两个(不一定是唯一的)数字的总和。例如,1 2 3 5是这样的链,2是1 + 1,3是2 + 1,5是2 + 3。找到以给定数字结尾的最小长度链。

分析:深度优先搜索迭代加深在这里运行良好,因为DFS倾向于首先尝试1 2 3 4 5 ... n,这非常糟糕,并且队列对于BFS来说非常快速地变得太大。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值