搜索
剪枝
- 优化搜索顺序:我们应该选择首先搜索分支较少的点
- 排除等效冗余
- 可行性剪枝:有些明显搜下去没结果的,直接剪枝
- 最优化剪枝:有些已经不可能是最优解的,直接剪枝
- 记忆化搜索
图论
求负环常用方法(基于SPFA)
- 统计每个点入队的次数,如果有点入队 n n n 次,说明有负环
- 统计当前每个点的最短路边数,如果有最短路边数大于等于 n n n ,则说明有负环
差分约束
求不等式的可行解
源点需要满足的条件:从源点出发,一定可以走到所有的边。
步骤:
- 先将每个不等式 x i ≤ x i + c k x_i\le x_i+c_k xi≤xi+ck 转化成一条从 x j x_j xj 到 x i x_i xi 长度为 c k c_k ck 的边
- 找一个超级源点,使得该源点可以遍历到图中所有的边
- 从源点求一次单源最短路
- 结果1:如果存在负环,原不等式一定无解
- 结果2:如果没有负环,则 d i s t [ i ] dist[i] dist[i] 就是原不等式的一个可行解
求最大值,最小值
结论:如果求的是最小值,则应该求最长路;如果求最大值,应该求最短路。
问题:如何转化 x i ≤ c x_i\le c xi≤c 其中 c c c 是一个常数,这类不等式
方法:建立一个超级源点0,让0指向 x i x_i xi ,并且边的长度为 c c c
以求 x i x_i xi 最大值为例:求所有从 x i x_i xi 出发,构成不等式链 $x_i\le x_j+c_1\le x_k+c_1+c_2\le···\le \sum c_i $ , 所计算出的上界。最终 x i x_i xi 的最大值等于所有上界的最小值。
求最近公共祖先
倍增(在线做法) O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn)
f a [ i , j ] fa[i,j] fa[i,j] 表示从 i i i 开始,向上走 2 j 2^j 2j 步所能走到的节点。 0 ≤ j ≤ l o g n 0\le j\le logn 0≤j≤logn
d e p t h [ i ] depth[i] depth[i] 表示节点的深度
这两个数组初始化使用 d f s dfs dfs 或 b f s bfs bfs ,每次使用二进制拼凑的思想。
哨兵:如果从 i i i 向上走 2 j 2^j 2j 步会跳过根节点,那么 f a [ i , j ] = 0 fa[i,j]=0 fa[i,j]=0 ;
步骤:
- 先将两个点跳到同一层
- 让两个点一起跳,直到跳到它们最近公共祖先的下一层。
预处理 O ( n l o g n ) O(nlogn) O(nlogn) + + + 查询 O ( l o g n ) O(logn) O(logn)
void bfs(int root){//预处理fa数组和depth数组
memset(depth,0x3f,sizeof depth);
depth[0]=0,depth[root]=1;//哨兵和根节点
int tt=0,hh=0;
q[0]=root;
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(depth[j]>depth[t]+1){
depth[j]=depth[t]+1;
fa[j][0]=t;
q[++tt]=j;
for(int k=1;k<=15/*log(N)上取整*/;k++)
fa[j][k]=fa[fa[j][k-1]][k-1];
}
}
}
}
int lca(int x,int y){//倍增法过程
if(depth[x]<depth[y])swap(x,y);
for(int i=15;i>=0;i--){
if(depth[fa[x][i]]>=depth[y])
x=fa[x][i];
}
if(x==y)return x;
for(int i=15;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
Tarjan(离线做法) O ( n + m ) O(n+m) O(n+m)
在进行深度优先搜索的时候,把所有点分为三类:
- 已经搜索过,并且已经回溯的点,标记为 2 2 2
- 正在搜索,还未回溯,标记为 1 1 1
- 没有搜索到的点,标记为 0 0 0
每次遍历到一个新点,把它合并到它的父节点。遍历询问时,当另外一个点已经回溯之后,那么最近公共祖先就是另一个点被合并到的点。
void tarjan(int u){
st[u]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!st[j]){
tarjan(j);
p[j]=u;
}
}
for(auto item:query[u]){
int y=item.first,id=item.second;
if(st[y]==2){
fa[id]=find(y);
}
}
st[u]=2;
}