最短路径总结

今天学习了《算法笔记》最短路径 最小生成树 拓扑排序部分 这里做简单总结
最短路径使用较多的是dijstra算法 基于贪心的思想 在无负边权的情况下,可以有效地求出单源最短路径,它维护d[]表示结点i到起点的距离 每次选择距离起点最近地结点访问,也就是d[i]最小的结点i进行访问,之后,使用结点i对未访问过的结点进行松弛,可以使用优先队列进行优化。在许多题目中,不会只考最短距离,一般会有第二,第三标尺,比如边权之和,点权之和,最短路径的条数等,以上这三个标尺可以在松弛过程中一并更新优化,关键是建立num[],它的含义是由起点到结点i的这条最短路径的边权和,点权和,最短路径条数,注意初值起点的值即可。这样,当结点被松弛,即d[to] > d[from] + cost,参数进行覆盖更新,当d[to] == d[from] + cost时,根据参数大小关系或相关逻辑进行更新。
但有时标尺多了之后,在函数内判断就导致不满足最优子结构,这时应该使用逻辑更为简单的算法dijstra + DFS 这个的思路就是使用dijstra算法求出所有的由终点到固定单源起点最短路,思想就是定义vector pre[] 它的定义是从起点到结点i的最短路径中结点i的前驱,它的更新也是在松弛过程中,当松弛成功,就清空原pre,压入松弛结点,当距离相同,则直接压入松弛结点。那我们就能想到只要从终点一直沿着前驱走,最终一定能够走到起点,由此便产生了一条完整的从终点到固定起点的最短路径。我们可以使用DFS对路径进行搜索,同时维护全局最优变量,当搜索到起点时,就判断条件,更新res,同时将最优变量更新为先路径的参数,注意,一套参数只表示一条路径,当第一标尺更新时,第二标尺一定要同时更新。
模板代码
dijstra 最短路径

void dijstra(gg st){
    priority_queue<arr2,vector<arr2>,greater<arr2>>q;
    fill(d.begin(),d.end(),INT_MAX);
    q.push({0,st});
    d[st] = 0;
    while(not q.empty()){
        auto n = q.top();
        q.pop();
        if(n[0] != d[n[1]]) continue;  //  防止旧部松弛
        for(auto& i : adj[n[1]]){
            if(d[n[1]] + i.len < d[i.u]){
                d[i.u] = d[n[1]] + i.len;
                num[i.u] = num[n[1]];
                w[i.u] = w[n[1]] + weight[i.u];
                q.push({d[i.u],i.u});
            }else if(d[n[1]] + i.len == d[i.u]){    //   这里这个被松弛的结点无需入队 因为距离相同 结点相同 
                if(w[n[1]] + weight[i.u] > w[i.u]) {
                    w[i.u] = w[n[1]] + weight[i.u];
                }
                num[i.u] += num[n[1]];
            }
        }
    }
}

spfa 最短路径

void spfa(gg st){
    queue<gg>q;
    q.push(st);
    fill(d.begin(),d.end(),INT_MAX);
    d[st] = 0;
    inq[st] = true;
    while(not q.empty()){
        gg p = q.front();
        q.pop();
        inq[p] = false;
        for(auto& e : adj[p]){
            if(d[e.u] > d[p] + e.len){
                d[e.u] = d[p] + e.len;
                num[e.u] = num[p];
                w[e.u] = w[p] + weight[e.u];
                if(not inq[e.u]){
                    q.push(e.u);
                    inq[e.u] = true;
                }
                pre[e.u].clear();
                pre[e.u].insert(p);
            }else if(d[e.u] == d[p] + e.len){
                if(w[e.u] < w[p] + weight[e.u]){
                    w[e.u] = w[p] + weight[e.u];
                }
                num[e.u] = 0;
                pre[e.u].insert(p);
                for(auto& i : pre[e.u]) num[e.u] += num[i];  //  这里需要重新计算  记前驱
                if(not inq[e.u]){   //  这里和dij不同 这里只是结点 没有距离 如果这个被松弛的点不在队中 也一定要入队
                    q.push(e.u);
                    inq[e.u] = true;
                }
            }
        }
    }
}

回溯求最短路径代码

void DFS(string s, gg happiness, gg num){
    if(s == st){
        if(happiness > maxhappy or (happiness == maxhappy and happiness * 1.0 / num > maxavg)){
            res = path;
            maxhappy = happiness;
            maxavg = happiness * 1.0 / num;
        }
        return;
    }
    happiness += happy[s];
    num++;
    for(auto i : pre[s]) {
        path.push_back(i);
        DFS(i,happiness,num);
        path.pop_back();
    }
}

Kruskal算法 求最小生成树

struct Edge{
    gg u, v,cost;
};
vector<Edge>edges;
vector<gg>ufs(maxn);
gg findFather(gg x){
    return x == ufs[x] ? x : ufs[x] = findFather(ufs[x]);
}
void unionSets(gg x, gg y){
    ufs[findFather(x)] = findFather(y);
}
void init(){
    iota(ufs.begin(),ufs.end(),0);
}
gg Kruskal(){
    gg res = 0;
    sort(edges.begin(),edges.end(),[](const Edge& a, const Edge& b){
        return a.cost < b.cost;
    });
    for(auto& e : edges){
        if(findFather(e.u) != findFather(e.v)){
            res += e.cost;
            unionSets(e.u,e.v);
        }
    }
    return res;
}

拓扑排序

bool topologicalSort(){
    vector<gg>res;
    queue<gg>q;
    for(gg i=0;i<n;i++){   //  压入入度为0的结点
        if(degree[i] == 0) q.push(i);
    }
    while(not q.empty()){
        gg u = q.front();
        res.push_back(u);   //  生成拓扑序列
        q.pop();
        for(auto i : adj[u]){
            degree[i]--;
            if(degree[i] == 0) q.push(i);
        }
    }
    if(res.szie() == n) return true;  //   n为结点个数
    return false;
}

DFS版拓扑排序

bool DFS(gg u){
    c[u] = -1;  //  访问标准 表示正在拓扑 还未拓扑成功的结点  初始值为0
    vector<gg>res;
    for(auto i : adj[u]){
        if(c[i] < 0) return false;  //  表示有环 拓扑失败
        else if(c[i] == 0 and not dfs(i)) return false;  //  后续结点拓扑失败
    }
    c[u] = 1;  //  表示拓扑成功
    res.push_back(u);
    return true;
}
bool topsort(){
    memset(c,0,sizeof(c));
    for(gg u =0;u<n;u++){
        if(not c[u]){
            if(not DFS(u)) return false;  //  图如果是连通图,从任意节点拓扑一次,若失败,即有环,若成功,即无环
        }
    }
    return true;  //  拓扑成功
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值