树、图的存储:链式前向星
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Edge{u,v,next,weight}和head[]辅助数组。
head[]存Edge的序号,为以对应顶点为起点的边。
Edge中,u:起点,v:终点,next:以u为起点的下一条边的序号,无边返回为-1
初始化:
edge[i].next=head[u]
head[u]=i
遍历:
for(int i = head[n];i!=-1;i=edge[i].next){
//操作edge[i].v
}
注:无向图edge要开2倍的大小
//图的其他数据结构:邻接矩阵(二维数组)、邻接表(Vector<Vector>)图的遍历:
DFS:递归实现,递归完要回退
BFS:队列优化单源点最短路径:SPFA算法(队列优化,不断松弛操作),可有负边不能负环。
或Dijkstra算法,不能负边or负环。一次将单源点到所有点的最小距离都生成了,
不断选最小点,更新未选点。图的最小生成树
prim算法:边较少。(不断从所有已选点中选距离最短的相邻未选点)
Kruskal算法:边较多。(所有边中不断找最小边,判断是否在一个连通分量中)
判断连通分量用到路径优化后的并查集,注意更新parent[]数组一定要更新根节点。
关于已选集合和未选集合的实现:可用visited数组来存储状态。在一些树形动态规划问题中,可能会子结点对应多个父结点,所以开Edge数组要开2倍的大小,而且在前向星便利时要除去父结点的情况。
动态规划要点
1.列状态转移方程,常分选择/不选择当前结点两种情况(用二维数组a[n][2]表示)
2.一定要将每种情况的值存储下来,这样才能保证效率。用二维数组存↑
3.程序上常常用递归或for循环实现归并排序:
【递归】的思想对数组进行排序,包括排序和合并两个步骤。
先将数组分为左右两部分,分别排序,再将有序的两部分合并。
可用一个辅助数组temp来临时保存数据来实现原数组排序。
可用归并排序来求逆序数:
一个数组的逆序数【左半部分逆序数】+【右半部分逆序数】+【左半部分比右半部分大的数的个数(在合并左、右时计算)】二叉树的操作中注意可能会出现单根的情况,会导致树深度大大增加。
此时考虑用二叉平衡树。
平衡二叉树的简化版->splay树,伸展树。
一些技巧
若对循环设置一个判断的变量,要注意在每次循环前/后是否需要对变量置为初始值。防止上一次的判断结果影响这一次的判断。
eg:<完美的代价>中n为奇数条件pos的使用。日期的计算:
计算从日期a到日期b的天数n
最常见如果a算在内但不算b那就是n=b-a。
eg:算天数差/经过了多少天/隔多少天
如果a和b这两天也算在内就是n=b-a+1
如果不算a和b这两天就是n=b-a-1for()循环,满足一定条件才执行段内语句,要在段内用if语句,不能将条件写到for()内,否则会变成中止条件,而不是continue条件。
一些实际的应用题,可用模拟的方法来求解。确定一个最小的变化单元,不断模拟。
eg:<龟兔赛跑>问题中时间一次+1s模拟对于结束情形的判断,若判断各种结束情况很复杂且题目涉及总数量,可改为直接检测数量,会简化许多
在递归中,若表达式涉及传入递归函数的参数,要先写回传入的变量,再写递归式。防止下一次递归中改变了变量的值。
在进行按位操作时,输入、输出顺序和运算顺序是相反的。
但输入输出可都用相反顺序(即正常读入读出顺序),最后反反得正,结果不受影响。关于输出空格/空行的控制,for循环下可对循环次数进行检测(首次/末次不输出),while循环下可设标志位检测。
大while中套小while,小while每次的判断条件也要保证大while的条件,保证小while的循环不会使大while“循环越界”。
Post Date: 2018-01-20
版权声明: 本文为原创文章,转载请注明出处