洛谷 P3371 单源最短路

dijkstra 算法

可以求出从指定出发点到其他所有点的最短路径,但不能包含负权的边。

设集合 s 为已经求出来的点的集合,集合 v 为剩余待求的结点。

要求从 a 点出发,求出到所有点的最小权值,为达到目标,要将 v 集合中的结点一个一个地添加到 s 结点,当所有结点都在 s 集合中时,就完成任务了。

初始化:s 集合中只有 a  结点,其他所有结点都在 v 集合中。

每一步:选出从 a 出发,经过 s 集合更新后(经过 s 集合中哪些结点后到达目的地的距离是最短的)且目标节点在 v 中的一条路,这条路就是到这个目标节点的最短路,因为如果再经过 v 中的某个子集更新后,这条路不可能会更短。

时间复杂度:O(n^2)   PS:在求到达 v 集合中路径最短的一个结点时,可以用堆优化。

代码#include <iostream>

#include <cstring>
#include <vector>
using namespace std;

const int MAX = 10005;
const int INF = 2147483647;

struct Node{
    int v, len;
    Node(){}
    Node(int nv, int nl) : v(nv), len(nl){}
};

int n, m, s;
int vis[MAX];
int dist[MAX];
vector<Node> G[MAX];

int main(){
//    freopen("input.txt", "r", stdin);
    
    scanf("%d%d%d", &n, &m, &s);
    for(int i=1; i<=m; i++){
        int u, v, len;
        scanf("%d%d%d", &u, &v, &len);
        G[u].push_back(Node(v, len));
    }
    
    //dijkstra算法
    memset(vis, 0, sizeof(vis));
    for(int i=1; i<=n; i++){
        dist[i] = INF;
    }
    dist[s] = 0;            //初始化,添加第一个点 
    vis[s] = 1;
    for(int i=2; i<=n; i++){        //添加第 2 到第 n 个点 
        for(int j=0; j<G[s].size(); j++){
            int u = s, v = G[u][j].v, len = G[u][j].len;
            dist[v] = min(dist[v], len + dist[u]);        //更新路径长度 
        }
        int minV, minLen = INF;
        for(int j=1; j<=n; j++){                //选出最短的 
            if(vis[j] == 0 && dist[j] < minLen){
                minLen = dist[j];
                minV = j;
            }
        }
        vis[minV] = 1;                    //添加这个点 
        s = minV;
    } 
    
    for(int i=1; i<=n; i++){
        if(i != 1)  printf(" ");
        printf("%d", dist[i]);
    }
    
    return 0;
}

 

 

SPFA 算法

有点像 BFS。

可以处理带负权边的图,但是图中不能存在负权回路。

初始化:起始点 a 加入队列。

每一步:当有一条路更新(有更短的路径)时,将该路径对应的结点(如果该结点在队列中,就不用重新放到队列了)加入队列(因为这条路更新了,所以可能通过这条路到达其他结点的距离也会更短)。当队列为空时,算法就结束了。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

struct Edge{
    int v, len;
    Edge(){}
    Edge(int nv, int nl) : v(nv), len(nl){}
};

const int MAX = 10005;
const int INF = 2147483647;

int n, m, s;
vector<Edge> G[MAX];
int vis[MAX];
int dist[MAX];


int main(){
//    freopen("input.txt", "r", stdin);
    
    scanf("%d%d%d", &n, &m, &s);
    for(int i=1; i<=m; i++){
        int u, v, len;
        scanf("%d%d%d", &u, &v, &len);
        G[u].push_back(Edge(v, len));
    }
    
    //SPFA 算法
    queue<int> que;            //存放结点的队列 
    memset(vis, 0, sizeof(vis));     //初始化 
    for(int i=1; i<=n; i++){
        dist[i] = INF;
    }
    vis[s] = 1;
    dist[s] = 0;
    que.push(s);
    while(!que.empty()){
        //进行松弛操作
        int u = que.front();  que.pop(); 
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i].v;
            int len = G[u][i].len;
            if(dist[v] > dist[u] + len){    //如果到 v 有更短的路径 
                dist[v] = dist[u] + len;
                if(vis[v] == 0){            //将 v 结点放入队列,有可能从 v 出发会有到达其他结点更短的路 
                    que.push(v);
                    vis[v] = 1;
                }
            }
        }
        vis[u] = 0;
    }
    
    for(int i=1; i<=n; i++){
        if(i != 1)  printf(" ");
        printf("%d", dist[i]);
    }
    
    return 0;
}

 

posted @ 2017-08-25 16:28  淡蓝色光  阅读(202)  评论(0编辑  收藏  举报