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

 

posted @ 2020-04-16 00:30  龙雪可可  阅读(180)  评论(0编辑  收藏  举报
****************************************** 页脚Html代码 ******************************************