Coins Respawn(负环,最短路)

题意

给定\(N\)个点\(M\)条边的有向图,对于每条边\((u_i, v_i)\),其边权为\(c_i\)

假设\(1\)号点到\(N\)号点的一条路径长度为\(d\),求\(d - T \times P\)的最大值。其中\(T\)为路径的边数,\(P\)为给定常数。

如果不存在最大值(陷入环中),则输出\(-1\)

题目链接:https://atcoder.jp/contests/abc137/tasks/abc137_e

数据范围

\(2 \leq N \leq 2500\)
\(1 \leq M \leq 5000\)

思路

对于这种与边数或者点数有关的问题,一个常规的套路是,将每条边的边权或点权减去\(P\)

对于每条边,边权减去\(P\),问题就转化成了从\(1\)\(N\)的最长路。将边权取反,即:\(P - c_i\),问题可以转化为从\(1\)\(N\)的最短路问题。

对于不存在最大值的情况,一定是陷入了负环当中。但是这不代表存在负环就一定不存在最大值。因为如果一个点在负环上,但是它并不能通往点\(N\),那么它不会出现在从\(1\)\(N\)的路径上。

那么我们需要判断的其实是能够通往点\(N\)的所有点中,是否存在位于一个负环上的。

因此,算法流程就是:先建立反图,然后通过BFS/DFS找到所有能够通往点\(N\)的点;由于可能存在负环,因此使用SPFA算法求出是否存在负环,如果不存在则求出最短路。

注意,使用SPFA算法的时候,从一个点向周围点进行拓展,只需要拓展能够通往点\(N\)的点。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 2510, M = 4 * N;

int n, m, p;
int h[N], hs[N], e[M], w[M], ne[M], idx;
int cnt[N], d[N];
bool st[N], connect[N];

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

void bfs()
{
    queue<int> que;
    que.push(n);
    connect[n] = true;
    while(que.size()) {
        int t = que.front();
        que.pop();
        for(int i = hs[t]; ~i; i = ne[i]) {
            int j = e[i];
            if(!connect[j]) {
                que.push(j);
                connect[j] = true;
            }
        }
    }
}

int spfa()
{
    queue<int> que;
    memset(d, 0x3f, sizeof d);
    d[1] = 0;
    st[1] = true;
    que.push(1);
    while(que.size()) {
        int t = que.front();
        que.pop();
        st[t] = false;
        for(int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if(connect[j] && d[j] > d[t] + w[i]) {
                d[j] = d[t] + w[i];
                if(!st[j]) {
                    st[j] = true;
                    que.push(j);
                    if(++ cnt[j] > n) return -1;
                }
            }
        }
    }
    return max(0, -d[n]);
}

int main()
{
    scanf("%d%d%d", &n, &m, &p);
    memset(h, -1, sizeof h);
    memset(hs, -1, sizeof hs);
    for(int i = 0; i < m; i ++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(h, a, b, p - c), add(hs, b, a, 1);
    }
    bfs();
    printf("%d\n", spfa());
    return 0;
}
posted @ 2022-06-15 15:33  pbc的成长之路  阅读(32)  评论(0编辑  收藏  举报