P5905 卡spfa只能用dj的有负环的全源最短路

传送门

题目描述:

给定一个包含 n 个结点和 m 条带权边的有向图,求所有点对间的最短路径长度,一条路径的长度定义为这条路径上所有边的权值和。

注意:

  1. 边权可能为负,且图中可能存在重边和自环;

  2. 部分数据卡 n 轮 SPFA 算法.   

思路:有负边,直接跑dj会出错,需要把边变为逻辑上的正数,不能简单得同时加上一个值,原因:here

创建一个节点,向其余节点连权值为0的边,然后以它为源点跑spfa,得到距离hi,顺便判断是否存在负环,存在负环则输出-1,

否则继续操作,将原图中的m条边进行处理,即e[i].w+=h[u]-h[v],然后再对每个点跑dj,计算结果时dis[v]要减去之前加上的h[u]-h[i];

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10005;
const int inf = 1000000000;
const ll mod = 100000000;
struct edge {
    int f, t, nxt;
    ll dis;
}e[maxn];
int hd[maxn], tot;
void add(int f, int t, ll dis) {
    e[++tot] = { f,t,hd[f],dis };
    hd[f] = tot;
}
int t[maxn];
bool vis[maxn];
ll dis[maxn], h[maxn];
int n, m;
bool spfa(int s) {
    memset(h, 0x3f, sizeof(h));
    queue<int>q;
    q.push(s);
    h[s] = 0;
    vis[s] = 1;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for (int i = hd[u]; i; i = e[i].nxt) {
            int v = e[i].t; ll w = e[i].dis;
            if (h[u] + w < h[v]) {
                h[v] = h[u] + w;
                if (!vis[v]) {
                    vis[v] = 1;
                    t[v]++;
                    if (t[v] == n + 1)return 0;//负环
                    q.push(v);
                }
            }
        }
    }
    return 1;
}
struct node {
    int id;
    ll dis;
    bool operator<(node b)const {
        return dis > b.dis;
    }
};
void dj(int s) {
    for (int i = 0; i <= n; i++) {
        dis[i] = inf;
    }
    memset(vis, 0, sizeof(vis));
    priority_queue<node>q;
    dis[s] = 0;
    q.push({ s,0 });
    while (!q.empty()) {
        int u = q.top().id; q.pop();
        if (vis[u])continue;
        vis[u] = 1;
        for (int i = hd[u]; i; i = e[i].nxt) {
            int v = e[i].t; ll w = e[i].dis;
            if (dis[u] + w < dis[v]) {
                dis[v] = dis[u] + w;
                q.push({ v,dis[v] });
            }
        }
    }
}
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int a, b; ll w; scanf("%d%d%lld", &a, &b, &w);
        add(a, b, w);
    }
    for (int i = 1; i <= n; i++) {
        add(0, i, 0);
    }
    if (!spfa(0)) {
        printf("-1\n"); return 0;
    }
    for (int i = 1; i <= m; i++) {
        int u = e[i].f, v = e[i].t;
        e[i].dis += h[u] - h[v];
    }
    for (int i = 1; i <= n; i++) {
        dj(i);
        ll ans = 0;
        for (int j = 1; j <= n; j++) {
            if (dis[j] == inf) {//不能到达
                ans += j * dis[j];
            }
            else {
                ans += j * (dis[j] + h[j] - h[i]);
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

posted @ 2021-05-26 18:08  cono奇犽哒  阅读(51)  评论(0编辑  收藏  举报