本周完成的任务如下:
搜索练习题目完善
树与图的存储和遍历理解与掌握
树图与搜索结合的题目练习
1.树与图的存储
一般来说用邻接表来实现存树、图(邻接矩阵的话时间复杂度n²)(其实用vector来实现也可以,不过用数组模拟效率更高,虽然开始时会有些不适应,但理解后毕竟效率更高些)。思路是每个结点开一个单链表,存该节点的连通点。建立两个数组,分别存值和下个连通节点的下标,两个数组通过下标建立联系(无向图的话每次存边存两次有向边即可)。插入新增结点的话思路如下(这里的插入理解了好长时间,所以画了个图,防止以后自己忘记了...)
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
2.树与图的遍历
dfs思路:传入一个结点,从当前节点开始,标记结点是否已经遍历过,若没有,则枚举所有该节点的出边(连通点),若未标记,则递归深搜。即可完成该点的遍历。若题目要求各种情况,则遍历图:对每个结点都进行一次深搜。
bfs思路:当前节点标记已被遍历,结点入队,循环队列非空,每次取出队头,将未标记的且与当前结点连通的结点入队,并标记已被遍历,即可完成当前节点的遍历。需要遍历图则对每个节点进行广搜。
这里树与图搜索区分于之前的搜索主要是如何表示将连通点入队(如何枚举所有连通点/出边)。代码实现如下:
//dfs:
//h代表头节点,u是当前访问的结点,e存值,ne存连通点下标,st标记
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j]) dfs(j);
}
//bfs:
for(int i=h[i];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[i]){
st[j]=true;
q.push(j);
}
}
其实本质上跟前面练习的深搜广搜没有区别,主要是表示连通点需要新的思考。
3.树图存储与搜索结合相关题目
题意:给出n 个点 m 条边的无向图,确定给定点与点间边的方向,使其生成一个拓扑序列,且使得这个拓扑序是在所有可能的拓扑序中字典序第 k小的。
先将给出的边连通关系用邻接表存好,再用拓扑排序生成拓扑序列,最后卡在寻找字典序第k小的拓扑序。这道题收获是巩固了一下拓扑排序代码,思路:
遍历结点数组,先将入度为0的点入队(建图同时开一个数组记录点的入度),循环队列非空,取出头节点,寻找入度为0结点的连通点,每搜到一次入度-1,入度为0时入队,开一个数组,每个结点入队时记录,即可生成拓扑序列。
题意:给出n种生物之间的m种吃与被吃的关系,求出食物链条数
高中生物知识,食物链即单向无环图,并且最左端是生产者,入度为0,可以想到拓扑排序,用一个答案数组记录每次入度为零的点入队即可。
题意:存在地窖相连,且每个地窖中都有一定数量的地雷,求最多能挖到的雷数(不可回头)
图的存储+dfs遍历,遍历图中所有点,深搜,如果可以继续搜索,则更新最大值和答案数组,每次寻找连通点,若未走过,则进行标记,递归,回溯取消标记(老套路了),dfs参数记录当前位置、已走结点数、已挖到的雷数。
题意:给出若干景点是否连通、穿梭两景点间所需时间,求耗时最长的方案。
即 无向图+边权的存储与遍历,求怎样走使边权加和最大。
这里存边权想了半天(其实存边权跟存其他的东西思路都一样,都是通过下标联系起来的),最后又是画图解决的(不得不说图真是帮助理解的神器..)
这里点的值就显得没必要了,根据边权来进行dfs(用sum来记录最大值,更新即可)
总结一下,拿出来的时间还是不够多,题单靠后面的题目都只进行了读题思考+大体看看题解。虽说搜索专项练习结束了,但好像最近学的东西都离不开搜索hhh
发完博客才想起来cf Round#783(div.2)A题题解忘记写了...
思路是这样的:分成两段走,先从(1,1)走到(n,n),然后接下来每次横坐标回退然后向下靠近目标点,分别得出奇偶的规律,然后把走的步数加和即可(当时手动演算了挺多,其实可以直接打表演算更快能找到规律...)