写这类搜索题。首先感觉要有个框架。比如我的框架对于BFS来说(对于DFS,我想有两个一个是递归版一个是栈版)。这里是BFS小结。所以介绍一下BFS。我的框架。(也是搜集了网上许多神人的作品。)
1:节点的定义。时间问题。步数。以及一系列其他基本动态属性都放在这里。先定义2个node now和next。
2:map map本身就可以简单地记录可以行走和不可以行走的单纯点。(因为有些还有条件点。)map一开始在外面围一圈不可行走的属性。(这个也是仿照某位大神)这个围一圈直接在初始化的时候全部都是非法的 然后在scanf的时候保留即可。方便快捷。
3:mark 这个标记数组可有可无。假如map不够用的时候加入。记得初始化!
4:dir 我觉得dir真的是很重要。很多不同的走法其实都可以放入在dir之中。根据不同的情况有不同的方向的走法。
5:flag,now的初始化。
框架成熟之后是注意点(搜索的题目往往代码量较大。所以这一点是很重要的):
1:队列清空问题!(有可能上一个测试例中会有残留)
2:终点和可行走点(我喜欢在now的时候判断。即。即使遇到终点也加入队列中取出来的时候统一判断)。
终点和可行走点都是可以加入的,所以我们总是放在一起。一个判断。但是当标记为不可行走的时候。终点假设是X。也被标记成了不可行走(用map标记的时候)。
所以永远都是No!所以在里面还要特判一下加入。比如以下.是可行走。X是终点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
if(map[next.x][next.y]=='.'||map[next.x][next.y]=='X') { que.push(next); if(map[next.x][next.y]=='.'){map[next.x][next.y] ='W';} }
3:next的赋值。请确保每一项都确定过!。
。。。(我还是新手待填充)
一系列不同问题:
1:相同区域个数。(DFS)
2:目标查找。(DFS)
3:规定时间内目标查找。(BFS)
4:查找最近目标(BFS,Rescue,这个正确的解法是目标来查找起点。也就是说我们要有个思维。目标和起点是能互换的。另外。HDU的数据很弱。这个好题啊。这个题目值得讲讲。毒药血药问题。)
。。。(我还是新手待填充)
分析BFS:
基础的BFS按照以上是能解决的。
而BFS往往会有别的条件,比如某种技能(穿墙,走两步,行和列坐标变化。。。)种种技能。同时这技能往往会弄上数值代表能使用的次数之类。
其实这些技能只是多了方向而已。多了种你能行走的方式。 当然。技能值越多往往是越好的(因为你有更多的选择,或者说因为有技能而有更快的方案,或者是不得不用技能的情况。比如前面有墙。你用穿墙术。)
那好。我们的首要任务明确了。分析什么是时间什么是技能值。其实这个很简单。只要看输出。要求什么最小即可。(两者都最小的话,我还没遇到过。)
什么最小什么就是时间除此之外,其实看看题目就能区分开来。
除此之外让我们看清楚BFS的的搜索过程。它是搜索在这个点能到达的点集的点。也就是说时间和它的关系是每一步增加一下时间。那么。技能只是在控制方向的话。我们并不需要去考虑因为技能而变得不再是简单的BFS类型。(当然那个技能不是做出什么让时间加一减一的事情。也许它可以通过穿墙使得时间变短,但这并不违反简单的BFS)那我们可以放心地只是在方向上进行改造。
NightMare:有一个数值是炸弹爆炸的数值 还有一个统计总时间。 我们要控制的量是总时间。所以这个是上述的时间。而炸弹爆炸其实应该抽象成一个技能。能够行走的技能。而亭子能够让技能恢复。
捡骨头:(学长原创题)大意就是你有一项可以走两步并且时间耗费只用1的技能(其实就是不影响时间的方向)。并且你可以借此来穿一层墙。这很明显。技能是走两步(方向拓展,我们只要在搜索的时候扩展一下方向)。
上述两类你会发现所谓技能只是让行走变得方便或者迟钝。总之就是方向上的控制(也许你会觉得前者感觉不像。但是你可以认为一开始你连走都不能走,所以爆炸了就果断continue。)。这两种都有个问题。也就是标记问题。同一个地方并不是走过就不能走的。一个很简单的例子。 你用技能走到了一个点。但是同样你可以不用技能走到这个点。前者没技能了。而后者有。 然而后续情况必须你有技能点。所以你不能用传统的标记手段了。你必须新NEW一个mark数组。然后去找最优的(也就是技能点最大的)情况。
其实除了经典的技能问题。还有别的问题也是如此。
只要抓住其根本。这些因素是影响方向的。而不是影响时间本身!就符合简单的BFS。
Rescue:有直接影响时间的因素。
你会发现。这个时候你这个点能相通的点集并不是时间最小的点。所以这并不符合简单的BFS。那么你就需要每次在放入点。拿出点的时候。要取时间最小的点。因为你可以到达的还是那样没变。然后你应该从时间最小的开始遍历。这个你就需要用到优先队列了。
优先队列的使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <queue> 2 struct Node 3 { 4 int x; 5 int y; 6 int time; 7 friend bool operator < (const Node &a,const Node &b) 8 { 9 return a.time>b.time; // > 的是对的 也就是返回小的。 10 } 11 }now,next; 12 priority_queue <Persion>q; 13
综上两种:分析一个问题。HDU-1180.诡异的楼梯
楼梯会随着时间而变动方向。
我们先考虑不变方向的楼梯。很明确。就是第一种情况。只是影响方向而已
如果这个楼梯会变方向。并且你并不能原地暂停。这个问题始终还是第一种情况。(即使你再一个返回还是不能通过。并且根据奇偶剪枝的知识。你会发现不能通过的点始终不能通过。即使你走了再复杂的路线。)
然而这个问题上我们可以等一秒再通行。这个原地暂停。
导致了不是简单的BFS。因为简单的BFS是你这个点能相通的点集并不是时间最小的点(同时很正常的只能搜索一次),因为你的点集会发生变化。(在楼梯前的那些点)所以你必须要2次才能搜索完它通的点集。或者需要的时间更多。
并且原地暂停这个并不适合加在方向上。因为无论是时间标记还是地点标记都不允许这类情况出现。即使加上去也太损耗效率。
所以利用优化队列转化成简单的BFS。
对楼梯处的方向处理。最难的地方就是原地了。怎么做到原地?
我的思路是直接走过去。如果是不行情况的楼梯。那么时间+2.如果是可行情况的楼梯。那么时间+1.
由于优先队列能帮忙调整。所以会符合。(其实即使是简单的BFS也能把普通队列改成优先队列来做。只是会耗费时间。)
别人的思路是。楼梯也是一个可达方块。时间在上面损耗。假如是符合的。还要处理时间-1。
其实观察我们对时间的处理。就能很简单发现这是第二类情况的问题了。尽管说原地暂停这个并不是十分明显。(其实考虑一下 原地暂停就是时间+1啊。)这样就能清楚明白了。
最后:同样可以解决二种情况结合的问题。标记和优先队列并不会冲突。
其实我是分析了在第一种情况中的一个特殊情况 也就是有技能值得情况。而普通梯子 和 空间传送器 之类的问题就是最最简单基础的BFS问题而已。 并且不用特殊标记时间,可以标记的是位置。