zoukankan      html  css  js  c++  java
  • spfa算法

    acwing 851: spfa求最短路

    给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。

    请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible。

    数据保证不存在负权回路。

    输入格式

    第一行包含整数n和m。

    接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

    输出格式

    输出一个整数,表示1号点到n号点的最短距离。

    如果路径不存在,则输出”impossible”。

    数据范围

    1≤n,m≤105,

    图中涉及边长绝对值均不超过10000。

    输入样例:

    3 3

    1 2 5

    2 3 -3

    1 3 4

    输出样例:

    2

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int N = 100010;
    int n, m, e[N], d[N], h[N], ne[N], idx;
    int w[N];
    bool st[N];
    void add(int a, int b, int c)
    {
        e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
    }
    int spfa()
    {
        memset(d, 0x3f, sizeof d);
        d[1] = 0;
        queue<int> q;
        q.push(1);
        st[1] = true;
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            st[t] = false;
            for(int i = h[t] ; i != -1; i = ne[i])
            {
                int j = e[i];
                if(d[j] > d[t] + w[i]){
                    d[j] = d[t] + w[i];
                    if(!st[j])
                    {
                        q.push(j);
                        st[j] = true;
                    }
                }
            }
        }
        if(d[n] == 0x3f3f3f3f) return -1;//这里不需要像bellman_ford里面一样/2,因为无穷大的边不会更新加入到队列当中来。
        return d[n];
    }
    int main()
    {
        cin>>n>>m;
        memset(h, -1, sizeof h);
        for(int i = 0;i<m;i++)
        {
            int x, y, z;
            cin>>x>>y>>z;
            add(x, y, z);
        }
        int t = spfa();
        if(t == -1) cout<<"impossible"<<endl;
        else cout<<t<<endl;
    }

    spfa是bellman_ford算法的改进,不会无脑的所有一把更新,而是更新成距离短的,和Dijkstra长得很像,都是用队列,两种算法的对比:

     可以看到Dijkstra采用的优先队列,最小堆,每次得到的队首元素都是连接当中最小的,然后进过一次就不允许再进了,但是spfa可以允许多次入队。

    852. spfa判断负环

    给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。

    请你判断图中是否存在负权回路。

    输入格式

    第一行包含整数n和m。

    接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

    输出格式

    如果图中存在负权回路,则输出“Yes”,否则输出“No”。

    数据范围

    1n20001≤n≤2000,
    1m100001≤m≤10000,
    图中涉及边长绝对值均不超过10000。

    输入样例:

    3 3
    1 2 -1
    2 3 4
    3 1 -4
    

    输出样例:

    Yes
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int N = 2001, M = 10001;
    int w[M], e[M], d[N], h[N], ne[M], cnt[N];
    int n, m, idx;
    bool st[N];
    void add(int a, int b, int c)
    {
        e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
    }
    int spfa()
    {
        //    memset(d, 0x3f, sizeof d);
        //    d[1] = 0;  //找的不是最短距离,不需要初始化为无穷大和1号节点
        queue<int> q;
        // q.push(1); //判断存不存在负环,但不是判断从1开始的,1可能到不了n号点,把所有的点放进来,只要存在负环就一定可以找到
        // st[1] = true;
        for(int i = 1;i<=n;i++)
        {
            st[i] = true;
            q.push(i);
        }
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            st[t] = false;
            for(int i = h[t] ; i != -1; i = ne[i])
            {
                int j = e[i];
                if(d[j] > d[t] + w[i]){
                    d[j] = d[t] + w[i];
                    cnt[j] = cnt[t] + 1;
                    if(cnt[j] >= n) return 1;
                    if(!st[j])
                    {
                        q.push(j);
                        st[j] = true;
                    }
                }
            }
        }
        //    if(d[n] == 0x3f3f3f3f) return -1;//这里不需要像bellman_ford里面一样/2,因为无穷大的边不会更新加入到队列当中来。
        return 0;
    }
    int main()
    {
        cin>>n>>m;
        memset(h, -1, sizeof h);
        for(int i = 0;i<m;i++)
        {
            int x, y, z;
            cin>>x>>y>>z;
            add(x, y, z);
        }
        int t = spfa();
        if(t) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
  • 相关阅读:
    vmware中三种网络连接方式
    【史上最全面经】各岗位汇总目录
    史上最全2019届秋招备战攻略
    程序员常用软件
    经典大数据面试题
    春招实习终于结束啦
    Java程序员自我介绍
    java一些面试题
    收割大厂offer需要具备的条件
    triangular distribution
  • 原文地址:https://www.cnblogs.com/longxue1991/p/12709797.html
Copyright © 2011-2022 走看看