最短路径

java 最短路dijkstra 求最短路径代码_动态规划

  1. 朴素dijkstra
  • 思路:
    第一步先初始化dist[1] = 0,dist[i] = INF (dist数组表示某一点到起点的距离);
    第二步第一层循环for循环 1 - n遍历所有点,然后第二层循环for 1 - n,找到 st[t] 值为 false 且距离起点最近的点的下标值赋给变量 t 并且把st[t]的值设为true( st 数组是标记这个点的最短路径是否已经找到,找到则为true);
    第三步用 t 更新其它所有点的距离。
  • 例题:
  • java 最短路dijkstra 求最短路径代码_算法_02

  • 代码实现:
#include <bits/stdc++.h>

using namespace std;

const int N = 510;
int n,m;
int g[N][N]; //存储每一条边的距离(邻接矩阵适合稠密图)
int dist[N]; //存储每一个点到起点的距离
bool st[N]; // 标记某一点的最短路径是否已经确定下来

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for(int i = 1; i <= n; i ++ )
    {
        int t = -1;
        for(int j = 1; j <= n; j ++ )
        {
            if(!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        }
        st[t] = true;
        for(int j = 1; j <= n; j ++ )
        {
            dist[j] = min(dist[j], dist[t] + g[t][j]);
        }
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(g, 0x3f, sizeof g);
    for(int i = 1; i <= m; i ++ )
    {
        int a,b,w;
        scanf("%d%d%d", &a, &b, &w);
        g[a][b] = min(g[a][b], w);
    }
    int res = dijkstra();
    printf("%d",res);
    return 0;
}
  1. 堆优化版dijkstra:
  • 优化了找最小值所需的时间;
  • 例题:
  • java 最短路dijkstra 求最短路径代码_java 最短路dijkstra_03

  • 代码实现:
#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;//<距离起点的距离,节点编号>
const int N = 150010;

int n,m,idx;//当前指到哪个邻接点
int h[N],w[N],e[N],ne[N];//e数组是存储
int dist[N];
bool st[N];

//在a节点之后插入一个b节点,权重为c
void add(int a,int b,int c)
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++ ;
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//所有距离初始化为无穷大
    dist[1] = 0;//1号节点距离起点为0
    priority_queue<PII, vector<PII>, greater<PII>> heap;//定义小根堆
    heap.push({0,1});//将1号节点插入堆
    while(heap.size())
    {
        auto t = heap.top();//取出堆顶顶点,因为是小根堆,所以堆顶就是最小值
        heap.pop();
        int ver = t.second, distance = t.first;
        if(st[ver]) continue;//如果已经确定最短就跳过这个节点
        st[ver] = true;
        for(int i = h[ver]; i != -1; i = ne[i] )
        {
            int j = e[i];//取出节点编号
            if(dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    while(m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a,b,c);
    }
    int res = dijkstra();
    printf("%d\n",res);
    return 0;
}
  1. Bellman_ford:
  • 思路:
    图用结构体实现,算法有两层循环实现,循环之前要初始化,第一层循环n次,第一层的含义是循环了几条边;第二层循环循环m次,更新距离,这里有个关键点就是要备份数组,用备份的数组去更新距离。
    备份在第一与第二层循环之间备份。
  • 例题:
  • 代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 510, M = 10010;
int n,m,k;
int dist[N];//意义跟上面dijkstra的一样
bool st[N];
int backup[N];//备份数组

struct edge{
    int a,b,w;
}edges[M];

int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for(int i = 0; i < k; i ++ )//因为题目限制不超过k条边
    {
        memcpy(backup,dist,sizeof dist);//一定要记得备份
        for(int j = 0; j < m; j ++ )
        {
            int a = edges[j].a;
            int b = edges[j].b;
            int w = edges[j].w;
            dist[b] = min(dist[b], backup[a] + w); 
        }
    }
    if(dist[n] > 0x3f3f3f3f / 2) return -2;
    return dist[n];
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0; i < m; i ++ )
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        edges[i] = {a,b,w};
    }
    int t = bellman_ford();
    if(t == -2) puts("impossible");
    else printf("%d\n",t);
    return 0;
}
  1. SPFA(基本可以解决dijkstra的题,但是如果卡住只能用堆优化的dijkstra;SPFA还可以判断是否有负权回路)
  • 思路:
    对bellman_ford的优化就是用队列实现只对需要更新的点进行更新,代码类似堆优化的dijkstra,只是st数组标注的是点是否已经在队列中,队列存的是待更新的点。
  • 例题:
  • java 最短路dijkstra 求最短路径代码_c++_04


  • 代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int h[N],w[N],e[N],ne[N],idx;
int dist[N]; 
bool st[N];//标志这个点是否已经在队列中
int n,m;

void add(int a,int b,int c)
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++ ;
} 

int spfa()
{
    memset(dist, 0x3f,sizeof dist);
    dist[1] = 0;
    queue<int> q;//队列存放待更新的点 
    q.push(1);
    st[1] = true;
    while(q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = false;
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if(!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    if(dist[n] == 0x3f3f3f3f) return -2;
    return dist[n];
}

int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    for(int i = 0; i < m; i ++ )
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int res = spfa();
    if(res == -2) puts("impossible");
    else printf("%d\n",res);
    return 0;
}
  1. Floyd:
  • 思路:
    基于动态规划,三维的数组实现。d[ i ] [ j ] [ k ],表示从 i 开始,经过1 - k个点到达 j 的最短距离,只是最后k被优化掉了,只剩下二维。
  • 例题:
  • java 最短路dijkstra 求最短路径代码_java 最短路dijkstra_05


  • 代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 210, INF = 1e9;

int n,m,k;
int d[N][N];

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

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= n; j ++ )
            if(i == j) d[i][j] = 0;
            else d[i][j] = INF;//这里的初始化有区别与上面几种算法,因为它是多源的
    for(int i = 0; i < m; i ++ )
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        d[x][y] = min(d[x][y],z);//存最小的边即可
    }
    floyd();
    while(k -- )
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(d[x][y] > INF / 2) puts("impossible");//因为有负的,所以只需大于 INF / 2
        else cout << d[x][y] << endl;
    }
    return 0;
}

以上是个人的一些看法,如果有错误的或者更好的做法,欢迎评论区给予更正谢谢!