算法知识总结(更新中)

搜索

剪枝

  1. 优化搜索顺序:我们应该选择首先搜索分支较少的点
  2. 排除等效冗余
  3. 可行性剪枝:有些明显搜下去没结果的,直接剪枝
  4. 最优化剪枝:有些已经不可能是最优解的,直接剪枝
  5. 记忆化搜索

图论

求负环常用方法(基于SPFA)

  1. 统计每个点入队的次数,如果有点入队 n n n 次,说明有负环
  2. 统计当前每个点的最短路边数,如果有最短路边数大于等于 n n n ,则说明有负环

差分约束

求不等式的可行解

源点需要满足的条件:从源点出发,一定可以走到所有的边。

步骤:

  1. 先将每个不等式 x i ≤ x i + c k x_i\le x_i+c_k xixi+ck 转化成一条从 x j x_j xj x i x_i xi 长度为 c k c_k ck 的边
  2. 找一个超级源点,使得该源点可以遍历到图中所有的边
  3. 从源点求一次单源最短路
    • 结果1:如果存在负环,原不等式一定无解
    • 结果2:如果没有负环,则 d i s t [ i ] dist[i] dist[i] 就是原不等式的一个可行解
求最大值,最小值

结论:如果求的是最小值,则应该求最长路;如果求最大值,应该求最短路。

问题:如何转化 x i ≤ c x_i\le c xic 其中 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 0jlogn

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

步骤:

  1. 先将两个点跳到同一层
  2. 让两个点一起跳,直到跳到它们最近公共祖先的下一层。

预处理 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)

在进行深度优先搜索的时候,把所有点分为三类:

  1. 已经搜索过,并且已经回溯的点,标记为 2 2 2
  2. 正在搜索,还未回溯,标记为 1 1 1
  3. 没有搜索到的点,标记为 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiutianhan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值