• 最短路



    求从 s 到 t 权值和最小的路径

    Floyd 算法:
    -
    多源最短路,求出所有点对的最短路长度
    -
    时间复杂度: O(n³)

    Dijkstra 算法:
    -
    单源最短路,求出某个点 s 到所有点的最短路长度
    -
    时间复杂度: O(n²)/O(mlogn)
    -
    无法处理负权

    SPFA 算法,即队列优化的 Bellman-Ford 算法:
    -
    单源最短路,求出某个点 s 到所有点的最短路长度
    -
    时间复杂度:声称为 O(m) ,最坏 O(nm) ,容易卡到最坏
    -
    可以处理负权边,可以判断负权环

    1:Floyd

    int d[MAXN][MAXN];

    void floyd() {
    for (int k = 1; k <= n; k++) {
    for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
    if (i != j && i != k && j != k)
    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    }
    }
    }
    }


    设 d[i][j][k] 为从 i 到 j ,仅通过编号为 1-k 的中间节点的最短路径距离

    d[i][j][k]=min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])

    初始值 d[i][j][0] 为两点之间边权值,未连通为 INF

    从 1 到 n 枚举 k ,然后枚举 (i, j)

    为了方便可以不开第三维,在原地迭代

    2:Dijkstra:

    未优化版:(自己写的可能错)

    #include<bits/stdc++.h>
    #define MAX 0x3f3f3f3f
    using namespace std;
    int n,m,s;
    int ls1,ls2,ls3;
    int dis[100005];
    bool vis[100005];
    int nn=0;
    vector<pair<int,int> > vec[100005];
    int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
    scanf("%d%d%d",&ls1,&ls2,&ls3);
    vec[ls1].push_back(make_pair(ls3,ls2));
    }
    for(int i=0;i<=n;i++)
    dis[i]=MAX,vis[i]=false;
    dis[s]=0;
    for(int i=1;i<n;i++){
    int x=0;
    for(int j=1;j<=n;j++)
    if(!vis[j]&&(x==0||dis[j]<dis[x]))x=j;
    vis[x]=true;nn++;

    int len=vec[x].size();
    for(int j=0;j<len;j++)
    if(dis[vec[x][j].second]>dis[x]+vec[x][j].first)
    dis[vec[x][j].second]=dis[x]+vec[x][j].first;
    }
    for(int i=1;i<=n;i++){
    printf("%d ",dis[i]);
    }


    return 0;
    }

    堆优化版:

    int d[MAXN];
    bool vis[MAXN];
    priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
    int dijkstra(int s, int t){
    memset(d, 0x3f, sizeof(d));
    d[s] = 0;
    q.push(make_pair(0, s));
    while(!q.empty()){
    int now = q.top().second;
    q.pop();
    if(!vis[now]){
    vis[now] = true;
    for(int i = he[now]; i; i = ne[i]){
    Edge& e = ed[i];
    if(d[e.to] > d[now] + e.dist){
    d[e.to] = d[now] + e.dist;
    q.push(make_pair(d[e.to], e.to));
    }
    }
    }
    }
    return d[t] == INF ? -1 : d[t];
    }


    起点作为已访问集合的第一个点,并更新相连的点的 dis
    -
    找到未被访问的 dis 最小的点,标记访问,用这个点更新相连的点 dis

    重复上述过程直到所有的点均被访问

    问题在于“找到未被访问的 dis ” 最小的点 这一步,两种不同的实现方法会带来两种复杂度

    枚举每个点

    当点 u 的距离更新时,将 (dis[u], u) 插入堆中,这样堆中可能有多个 u ,此时取出后
    面的点时,会发现 u 已经被访问过不再处理

    3:SPFA:

    bool inq[MAXN];
    queue<int> q;
    inline int spfa(int s, int t) {
    q.push(s);
    inq[s] = true;
    memset(d, 0x3f, sizeof(d));
    d[s] = 0;
    while (!q.empty()) {
    int now = q.front(); q.pop();
    inq[now] = false;
    for (int i = he[now]; i; i = ne[i]) {
    Edge &e = ed[i];
    if (d[now] + e.dist < d[e.to]) {
    d[e.to] = d[now] + e.dist;
    if (!inq[e.to]) {
    q.push(e.to);
    inq[e.to] = true;
    }
    }
    }
    }
    return d[t] == INF ? -1 : d[t];
    }


    Bellman-Ford :对整张图进行 n-1 次松弛,每次枚举每条边进行松弛,最后一定能得出
    最优解

    SPFA :在上述过程中避免无意义的松弛

    只有成功的松弛操作才会对那个点产生影响,所以使用队列维护等待松弛的点,每次取出
    一个点进行松弛,对于所有松弛成功的点加入队列

    判负环:某个点松弛了第 n 次,说明有负环

    //当最短路为单向路,可以将入边和出边分开记录,以求出到一个点来和去的最短路径

  • 相关阅读:
    Linux文件与目录管理(一)
    Linux文件基本属性
    软工实践总结
    微软必应词典的调查与研究
    调研安卓开发环境的发展演变
    软件工程的预定目标
    学习进度第5周
    机械学习----KNN算法
    MyBatis:简介、第一个程序01(纯小白非常实用)
    解决数据库连接时区的问题
  • 原文地址:https://www.cnblogs.com/zyfltyyz/p/11719178.html
Copyright © 2020-2023  润新知