图论最短路径问题

2020-08-10  本文已影响0人  yuq329

图论最短路径问题

最最原始的问题——两点间的最短路
这类背景一般是类似:已知各城市之间距离,请给出从城市A到城市B的最短行车方案 or 各城市距离一致,给出需要最少中转方案。

深度优先搜索(dfs)

基本模型:

void dfs(int step)
{
    判断边界
    尝试每一种可能 for(int i = 1; i <= n; i++)
    {
        继续下一步 dfs(step+1)
    }
}

广度优先搜索

int bfs()
{
    queue<pair<int,int>> que; //pair记录城市编号和dis,也可以用结构体
    que.push({1,0}); //把起始点加入队列
    book[1] = 1; //标记为已在路径中
    while(!que.empty()) 
    {
        int cur = que.front();
        que.pop();
        for(int i = 1; i <= n; i++)
        {
            if(e[cur][i] != MAX && book[i] == 0) //若从cur到i可达且i不在队列中,i入队
            {
                que.push({i, cur.second+1});
                book[i] = 1;
                if(i == n) return cur.second; //如果已扩展出目标结点了,返回中转城市数答案
            }
        }
    }
}

Floyd算法

for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
    {
        if(e[i][j] > e[i][1]+e[1][j]) e[i][j] = e[i][1]+e[1][j];
    }
for(int k = 1; k <= n; k++)
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(e[i][j] > e[i][k]+e[k][j]) 
                e[i][j] = e[i][k]+e[k][j];

单源最短路 Dijkstra

for(int i = 1; i <= n; i++) dis[i] = e[1][i]; //初始化dis为源点到各点的距离
for(int i = 1; i <= n; i++) book[i] = 0; 
book[1] = 1; //初始时P集合中只有源点

for(int i = 1; i <= n-1; i++) //做n-1遍就能把Q遍历空
{
    int min = INF;
    int u;
    for(int j = 1; j <= n; j++) //寻找Q中最近的结点
    {
        if(book[j] == 0 && dis[j] < min)
        {
            min = dis[j];
            u = j;
        }
    }
    book[u] = 1; //加入到P集合
    for(int v = 1; v <= n; v++) //对u的所有出边进行松弛
    {
        if(e[u][v] < INF) 
        {
            if(dis[v] > dis[u] + e[u][v]) 
                dis[v] = dis[u] + e[u][v];
        }
    }
}

Bellman-Ford算法

for(int k = 1; k <= n-1; k++)
    for(int i = 1; i <= m; i++)
        if(dis[v[i]] > dis[u[i]] + w[i])
            dis[v[i]] = dis[u[i]] + w[i];
for(int i = 1; i <= n; i++) dis[i] = INF;
dis[1] = 0; //初始化dis数组,只有1号的距离为0

for(int k = 1; k <= n-1; k++) //进行n-1轮松弛
    for(int i = 1; i <= m; i++) //枚举每一条边
        if(dis[v[i]] > dis[u[i]] + w[i]) //尝试进行松弛
            dis[v[i]] = dis[u[i]] + w[i];
for(int i = 1; i <= m; i++) 
    if(dis[v[i]] > dis[u[i]] + w[i]) flag = 1;
for(int k = 1; k <= n-1; k++) //进行n-1轮松弛
{
    bool check = 0;
    for(int i = 1; i <= m; i++) //枚举每一条边
        if(dis[v[i]] > dis[u[i]] + w[i]) //尝试进行松弛
        {
            dis[v[i]] = dis[u[i]] + w[i];
            check = 1;
        }
    if(check == 0) break;
}  

SPFA算法(队列优化的Bellman-Ford)

for(int i = 1; i <= n; i++) book[i] = 0; //初始时都不在队列中
queue<int> que;
que.push(1); //将结点1加入队列
book[1] = 1; //并打标记

while(!que.empty())
{
    int cur = que.empty(); //取出队首
    for(int i = 1; i <= n; i++) 
    {
        if(e[cur][i] != INF && dis[i] > dis[cur]+e[cur][i]) //若cur到i有边且能够松弛
        {
            dis[i] = dis[cur]+e[cur][i]; //更新dis[i]
            if(book[i] == 0) //若i不在队列中则加入队列
            {
                que.push(i);
                book[i] = 1;
            }
        }
    }
    
    que.pop(); //队首出队
    book[cur] = 0;
}

最短路径算法的对比

Reference

上一篇下一篇

猜你喜欢

热点阅读