最短路算法及模板

最短路算法及模板

1. 最短路算法概念及解决的问题

    所谓最短路问题,实际上就是求从一个节点开始到另外一个节点结束的最短路径。
    这里面需要解释一些词汇:
        1.  源点<=>起点
        2.  汇点<=>终点
        3.  n表示图内顶点的数量
        4.  m表示图内边的数量

2. 最短路算法的类型

img

    以上的算法在不同的情况下,有着不同的应用。要根据具体问题,选择合适的算法。
        1.  如果图是稠密图(边很多,点很少)且边权都是正数,那么用朴素Dijkstra即可。
        2.  如果图是稀疏图(边很少)且边权都是正数,那么用堆优化Dijkstra即可。
        3.  如果存在负权边,一般而言用SPFA算法即可。
        4.  如果存在负权边,且对边数存在限制(例如:求某一个点到其余所有点且边数不超过k的最短路径),那么用Bellman-Ford算法。
        5.  如果是多源汇最短路,用Floyd算法即可。
    接下来,依次对以上算法进行讲解。

3. 朴素Dijkstra算法思想

img
img

    朴素Dijkstra算法的目标是求:1号点到其余所有点的最短路径(距离)。
    朴素Dijkstra算法的执行思想如下:
        1.  初始化距离
            其中:dist[1] = 0
                  代表起点到起点的最短距离为0dist[i] = +∞
                  代表除起点之外的其余点到起点的距离为正无穷。
        2.  对顶点(1-n)进行for循环,在循环的过程中我们要做如下操作:
            2.1 取出不在s中的距离最近的顶点t。其中,s集合代表当前已确定最短距离的点。
            2.2 将顶点t纳入到s中
            2.3 用t来更新其它点x的距离。具体的更新思路就是:如果起点经过t中转后到达x的距离比起点直接到达x的距离要更近,那么就更新距离,否则不更新距离。
                用代码表示:
                If dist[x] > dist[t] + w。
                    dist[x] = dist[t] + w。
                其中,w代表t->x这条边的权重。
    根据上述的过程,我们可以发现:每一次循环,都可以确定一个点的最短距离。因此,循环n次就可以得到起点到所有点的最短距离。

4. 朴素Dijkstra算法举例

    红颜色代表还没有确定最短距离的点。
    绿颜色代表已经确定最短距离的点。

img
img
img
img
img
img

    至此,算法结束。

5. 朴素Dijkstra算法模板

int g[N][N];  // 存储每条边
int dist[N];  // 存储1号点到每个点的最短距离
bool st[N];   // 存储每个点的最短路是否已经确定

// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用t更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

6. 朴素Dijkstra算法例题

https://www.acwing.com/activity/content/problem/content/918/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n,m;

const int N = 510;
//稠密图,边很多,建议邻接矩阵来存储
int g[N][N];
//st数组代表某个点是否已经确定了最短距离
bool st[N];
//dist数组代表代表顶点到起点的最短距离
int dist[N];


int dijkstra(){
    //初始化最短距离为正无穷
    memset(dist,0x3f,sizeof(dist));
    //将起点到起点的最短距离初始化成0
    dist[1] = 0;
    for(int i=1;i<=n;i++){
        //t代表当前距离最短的点
        int t = -1;
        //在还没有确定最短距离的点中,找到距离最短的点
        for(int j=1;j<=n;j++){
            if(!st[j] && (t == -1 || dist[t] > dist[j])){
                t = j;
            }
        }
        //代表点t已经确定了最短距离
        st[t] = true;
        //用当前已经确定了最短距离的点t来更新其他点的最短距离
        for(int j=1;j<=n;j++){
            dist[j] = min(dist[j],dist[t] + g[t][j]);
        }
    }
    //代表起点1和终点n不连通
    //换句话说,路径不存在
    if(dist[n] == 0x3f3f3f3f){
        return -1;
    }
    return dist[n];
}




int main(){
    scanf("%d%d",&n,&m);
    //初始化图的权重初始都为正无穷
    memset(g,0x3f,sizeof(g));
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //对于重边,我们只需要保留权重最小的边即可。
        //对于自环,在求最短距离时可以忽略。
        g[a][b] = min(g[a][b],c);
    }
    int res = dijkstra();
    printf("%d",res);
    return 0;
}

7. 堆优化dijkstra算法思想

img
img
img

    堆优化dijkstra算法的优化思路:
        在上述朴素dijkstra算法中,最花费时间的步骤实际上是找到不在s中的距离最近的点。因此,堆优化dijkstra算法主要在这一步进行了优化。由于我们要找到不在s中的距离最近的点。因此,我们可以用小根堆来进行处理。这样的话,时间复杂度就得到了改善:O(1),由于循环n次,因此这一步就是n次。但是,为了维护小根堆,每次用t来更新其它点的距离时,我们也需要对堆来进行调整。每次调整堆的时间复杂度是O(log2n),更新距离为m次,因此总的时间复杂度为O(mlog2n)。具体请见上图。
    注意:在这个算法中,堆可以采用两种方式:
            1.  手写堆
            2.  c++的优先级队列
          在这里,我们采用优先级队列的写法。时间复杂度仍然是O(mlog2n)。
    堆优化dijkstra算法的执行流程跟朴素dijkstra算法类似,具体改变的地方可以见上述内容,这里不再赘述。

8. 堆优化dijkstra算法模板

typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    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];
}

9. 堆优化dijkstra算法例题

https://www.acwing.com/problem/content/852/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int N = 1000010;
typedef pair<int,int>  PII;

int h[N],e[N],ne[N],w[N],idx=0;
int n,m;
bool st[N];
int dist[N];

priority_queue<PII,vector<PII>,greater<PII>> heap;

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

//堆里存储pair,第一个参数代表距离,第二个参数代表点
int dijkstra(){
    dist[1] = 0;
    heap.push({dist[1],1});
    while(heap.size()){
        PII t = heap.top();
        heap.pop();
        //取出节点和距离
        int ver = t.second;
        int val = t.first;
        //如果该节点已经确定了最短距离,那么就不需要后续的操作
        if(st[ver]){
            continue;
        }
        //代表该节点确定了最短距离
        st[ver] = true;
        //更新与该节点邻接的其余点距离
        for(int i=h[ver];i!=-1;i=ne[i]){
            //j代表该节点的邻接节点
            int j = e[i];
            if(dist[j] > val + w[i]){
                dist[j] = val + w[i];
                heap.push({dist[j],j});
            }
            
        }
    }
    
    if(dist[n] == 0x3f3f3f3f){
        return -1;
    }
    
    return dist[n];
    
}

int main(){
    scanf("%d%d",&n,&m);
    memset(dist,0x3f,sizeof(dist));
    memset(h,-1,sizeof(h));
    for(int i=0;i<m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    printf("%d",dijkstra());
    return 0;
}

10. Bellman-Ford算法思想

img

    Bellman-Ford算法的执行流程如下:
        1.  遍历所有点
        2.  在遍历所有点的基础上,遍历所有边,同时更新距离。更新距离的方式跟dijkstra算法类似。
            dist[b] = min(dist[b],dist[a] + w);
            如果原点到b的距离比原点到a的距离+a到b的距离要更远,那么就更新为:dist[b] = dist[a] + w; 否则不变即可。
    经过证明发现:当Bellman-Ford算法循环完n次后,对于图中的所有边一定满足如下不等式:
        dist[b] <= dist[a] + w;
    上述的不等式也称为三角不等式。
    注意:如果图中存在负权回路,那么从1号点到n号点的最短距离不一定存在。因此,如果某一个图中的每一个点均存在最短距离,那么这个图在一般情况下是不会存在负权回路的。
          Bellman-Ford算法是有其实际意义的。如果外层循环循环了k次,那么内层循环中的距离的实际意义就是:从1号点经过不超过k条边到达所有节点的最短距离。
          Bellman-Ford算法是可以求出某一个图中是否存在负权回路的。证明如下:
          假设外层循环循环到了第n次,导致了内层循环的距离更新。那么就代表求出了从1号点经过不超过n条边到达所有节点的最短距离。而n条边需要n+1的点,而点只有n个。根据抽屉原理,至少有两个点是相等的。因此,图中存在负权回路。(了解即可)
    Bellman-Ford算法适合求有边数限制的最短路。并且,在图中可以存在负权回路。但是spfa算法不允许图中存在负权回路。

img

    Bellman-Ford算法的串联问题以及解决方式:
        给定上述的图,我们要求从原点经过不超过1条边到达3号节点的最短距离。那么,根据上图我们很容易得出答案是3而不是2。因为只能经过一条边。
        我们假设初始情况就是上述的第一张图。当我们遍历第一条边(1->2)时,上述的数组就变成了:
            1   2   3
    dist    0   1   +∞
        当我们遍历第二条边(2->3)时,根据上述的更新距离公式,距离就会变成:
            1   2   3
    dist    0   1   2
        这样显然是不对的。因为我们只允许经过一条边。这样的话,就不满足Bellman-Ford算法的要求了。
    因此,为了解决上述问题,我们引入备份数组。
        当我们遍历第二条边时,我们提前存入backup数组,backup数组存储的是上一次循环的更新情况。而不是这一次循环的更新情况。
        上一次循环更新情况的backup数组内容:
              1   2   3
    backup    0   +∞  +∞
        这一次循环更新情况的dist数组内容:
              1   2   3
    dist      0   1   +∞
        当我们更新距离的时候,我们只用backup数组来进行更新,而不用这次循环更新的dist数组,这样的话就避免了串联问题。即,
        当遍历第一条边时,
        dist[2] = min(dist[2],backup[1] + w)  其中,w代表1->2这条边的权重1。
        根据backup数组,dist[2] = +∞ backup[1] = 0。因此dist[2] = 1;
        当遍历第二条边时,
        dist[3] = min(dist[3],backup[2] + w)  其中,w代表2->3这条边的权重1。
        根据backup数组,dist[3] = +∞ backup[2] = +∞,因此dist[3]就是+∞。
        当遍历第三条边时,我们同样只用backup数组来进行更新,而不用这次循环更新的dist数组。即,
        dist[3] = min(dist[3],backup[1] + w) 其中,w代表1->3这条边的权重3
        根据backup数组,dist[3] = +∞,backup[1] = 0 w代表3。因此,dist[3] = 3;
        这样的话,就解决了串联问题。最终的dist数组:
        1   2   3
dist    0   1   3

11. Bellman-Ford算法模板

int n, m;           // n表示点数,m表示边数
int dist[N];        // dist[x]存储1到x的最短路距离
int backup[N];

struct Edge     // 边,a表示出点,b表示入点,w表示边的权重
{
    int a, b, w;
}edges[M];

// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    // 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。
    for (int i = 0; i < n; i ++ )
    {
        memcpy(backup,dist,sizeof(dist));
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }
}

int main(){

    ...
    bellman_ford();
    //由于图中会存在负权边,因此在遍历边的过程中可能会将0x3f3f3f3f进行缩小,因此不能使用dist[n] == 0x3f3f3f3f。
    //从1节点找不到n节点的最短路径
    if(dist[n] > 0x3f3f3f3f / 2){
        ...
    }else{
        printf("%d",dist[n]);
    }

    return 0;
}

12. Bellman-Ford算法例题

https://www.acwing.com/problem/content/855/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
const int M = 10010;

int n,m,k;

//存储边的信息
struct{
    int a;
    int b;
    int w;
}Edge[M];

int dist[N],backup[N];

void bellman_ford(){
    memset(dist,0x3f,sizeof(dist));
    dist[1] = 0;
    for(int i=1;i<=k;i++){
        //进行备份
        memcpy(backup,dist,sizeof(dist));
        for(int j=1;j<=m;j++){
            int start = Edge[j].a;
            int end = Edge[j].b;
            int val = Edge[j].w;
            dist[end] = min(dist[end],backup[start] + val);
        }
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        Edge[i].a = x;
        Edge[i].b = y;
        Edge[i].w = z;
    }
    bellman_ford();
    if(dist[n] > 0x3f3f3f3f / 2){
        printf("impossible");
    }else{
        printf("%d",dist[n]);
    }
    return 0;
}

13. spfa算法思想

    spfa算法是Bellman-Ford算法的优化。由于Bellman-Ford算法会遍历每一条边来计算最短路。但是,在遍历每一条边的过程中,有时候dist[b]的值并不会改变。在什么时候,dist[b]的值会进行改变呢?根据上述公式:
        dist[b] = min(dist[b],dist[a] + w);
    我们发现,只有dist[a]变小时,dist[b]的值才会改变(变小)。因此,spfa算法就是在这种情况下进行优化。
    spfa算法主要采用队列来对Bellman-Ford算法进行优化。spfa算法的步骤如下:
        1.  首先将起点加入队列。
        2.  while 队列不空
        3.      从队列中取出一个点
        4.      将该点从队列中删除
        5.      更新该点的所有出边,更新方式跟Bellman-Ford算法类似。
        6.      如果该点的出边更新成功,那么就将该点出边所邻接的点加入队列。
        7.  持续上述过程,直到队列空为止。
    spfa算法优化的核心思路就在于:spfa算法并不是遍历每一条边,只有源点到某一个点的最短路更新时,我们才需要遍历该点所邻接的边进行更新。如果源点到某一个点的最短路没有更新的话,那么该点所邻接的边也不会进行更新。因此,spfa算法对所遍历的边进行了优化,时间复杂度更小了。
    需要注意的是,有些正权图也可以使用spfa算法来处理。spfa算法不存在bellman-ford算法的串联问题。spfa算法在处理正权图时,效率比dijkstra算法要高,但是很容易被卡掉。spfa算法还可以去判负环。在接下来的部分进行进一步的讲解。

14. spfa算法模板

int n;      // 总点数
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储每个点到1号点的最短距离
bool st[N];     // 存储每个点是否在队列中

// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        auto 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])     // 如果队列中已存在j,则不需要将j重复插入
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

15. spfa算法例题

https://www.acwing.com/problem/content/853/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1000010;

int h[N],e[N],ne[N],w[N],idx = 0;

int n,m;

//st代表当前元素是否在队列中
int dist[N];
bool st[N];

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

int spfa(){
    memset(dist,0x3f,sizeof(dist));
    queue<int> q;
    dist[1] = 0;
    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];
                //如果j已经在队列里,那么就不需要重复更新。
                if(!st[j]){
                    //更新之后入队列,队列中的元素都是待更新边的元素
                    q.push(j);
                    //代表当前元素已在队列中
                    st[j] = true;
                }
            }
        }
    }
    return dist[n];
}



int main(){
    memset(h,-1,sizeof(h));
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    int res = spfa();
    if(res == 0x3f3f3f3f){
        printf("impossible");
    }else{
        printf("%d",res);
    }
    return 0;
}

16. spfa算法判断负环思想

img

    spfa算法还可以用来判断负环。具体步骤如下:
        首先我们需要解释以下两个数组:
            1.  dist[x] 表示当前情况下,从1号点到x号点的最短距离。
            2.  cnt[x]  表示当前情况下,从1号点到x号点所走的边数。
        那么,当距离更新时,边数也需要进行更新。即:
            If dist[x] > dist[t] + w[i]
                dist[x] = dist[t] + w[i];
                cnt[x] = cnt[t] + 1;
        具体可参照上图。
    spfa判断负环的思路就是:如果在求解最短路径的过程中,发现cnt[x] >= n。这句话的意思就是从1号点到x号点的边数大于等于n。大于等于n的边需要大于等于n+1个节点,而节点数只有n个。根据抽屉原理,至少两个节点是相等的。因此,图中存在环路,且一定是负环。这个过程跟Bellman-Ford算法判断负环的过程是一样的。只不过spfa算法判断负环的时间复杂度要更低。

17. spfa算法判断负环模板

int n;      // 总点数
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N], cnt[N];        // dist[x]存储1号点到x的最短距离,cnt[x]存储1到x的最短路中经过的点数
bool st[N];     // 存储每个点是否在队列中

// 如果存在负环,则返回true,否则返回false。
bool spfa()
{
    // 不需要初始化dist数组
    // 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。

    queue<int> q;
    //将所有点均加入到队列中,这样就可以找到这个图中的负环,而不是从某一个点开始的负环。
    for (int i = 1; i <= n; i ++ )
    {
        q.push(i);
        st[i] = true;
    }

    while (q.size())
    {
        auto 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];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;       // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return false;
}

18. spfa算法判断负环例题

https://www.acwing.com/activity/content/problem/content/921/
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;

int h[N],ne[N],e[N],w[N],idx = 0;
//cnt代表存储边数
int dist[N],cnt[N];


bool st[N];
int n,m;

void add(int x,int y,int z){
    e[idx] = y;
    w[idx] = z;
    ne[idx] = h[x];
    h[x] = idx++;
}

bool spfa(){
    memset(dist,0x3f,sizeof(dist));
    queue<int> queue;
    for(int i=1;i<=n;i++){
        queue.push(i);
        st[i] = true;
    }
    while(queue.size()){
        int t = queue.front();
        queue.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];
                cnt[j] = cnt[t] + 1;
                if(cnt[j] >= n){
                    return true;
                }
                if(!st[j]){
                    queue.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return false;
}


int main(){
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof(h));
    for(int i=0;i<m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    if(spfa()){
        printf("Yes");
    }else{
        printf("No");
    }
    return 0;
}

19. Floyd算法思想

img

    Floyd算法主要用来解决多源汇最短路问题。它采用邻接矩阵的形式把图存储下来。换句话说,就是用邻接矩阵来存储所有的边。例如,d[i,j]存储的就是从i到j的权重为d[i,j]的边。当Floyd算法执行完毕之后,d[i,j]就代表从i到j的最短路径。Floyd算法是基于动态规划的,有关动态规划的相关问题我们在动态规划这一节中进行详细讲解。
    Floyd算法允许图中有负权边,但是不允许图中有负权回路。

20. Floyd算法模板

//初始化: 自己到自己的最短距离为0,其余为无穷大
    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;

// 算法结束后,d[a][b]表示a到b的最短距离
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]);
}

21. Floyd算法例题

https://www.acwing.com/problem/content/856/
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 210;
const int INF = 0x3f3f3f3f;
int n,m,Q;
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,&Q);
    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=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(x != y){
            d[x][y] = min(d[x][y],z);
        }
    }
    floyd();
    while(Q--){
        int x,y;
        scanf("%d%d",&x,&y);
        if(d[x][y] > INF/2){
            printf("impossible\n");
        }else{
            printf("%d\n",d[x][y]);
        }
    }
    
    
    return 0;
}
    作者:gao79138
    链接:https://www.acwing.com/
    来源:本博客中的截图、代码模板及题目地址均来自于Acwing。其余内容均为作者原创。
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @   夏目^_^  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示