P5960 【模板】差分约束算法 · spfa

题解

学习博客↓

题解 P5960 【【模板】差分约束算法】

记几个重点:

差分约束可以用最短路做,跑一遍最短路,此时最短路的答案 d i d_i di​,也正是原不等式组的一个解 x i x_i xi

差分约束问题可以转化为最短路或最长路问题

连边后求最短路 :
x j − x i ≤ k x_j-x_i\leq k xjxik 变形为 x j ≤ x i + k x_j \leq x_i+k xjxi+k即从 i i i j j j 连一条边权为 k k k 的边,加入超级源点后求最短路,得到 x i ≤ 0 x_i\leq 0 xi0 所有 x x x 最大解 即所有解都是小于等于0的

连边后求最长路 :
x j − x i ≤ k x_j-x_i\leq k xjxik 变形为 x i ≥ x j − k x_i \ge x_j-k xixjk即从 j j j i i i 连一条边权为 − k -k k 的边,加入超级源点后求最长路,得到 x i ≤ 0 x_i\leq 0 xi0 所有 x x x 最小解 即所有解都是大于等于0的

当图中出现正环,求最长路时,方程组无解
当图中出现负环,求最短路时,方程组也无解

这里求最短路最长路都可以


P5960 【模板】差分约束算法 · spfa_第1张图片


代码

#include 
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, m, K;

namespace SPFA { //spfa 板子
    struct edge {
        int to, next;
        int w;
    } e[N];
    int head[N], tot;

    void add(int u, int v, int w) {
        e[++tot] = {v, head[u], w};
        head[u] = tot;
    }
    
    /* spfa实现方法:
        1.开一个队列 先将开始的节点放入
        2.每次从队列中取出一个节点u
          遍历与u相通的v节点 查询比对dis[v] 和 dis[u]+w[u,v]
          如果dis[v]>dis[u]+w  说明需要更新操作
                    1.存入最短路
                    2.由于改变了原有的长度 所以需要往后更新 与这个节点相连的最短路
                      (即:判断下是否在队列 在就不用重复 不在就加入队列 等待更新)
                    3.在这期间可以记录这个节点的进队次数,判断是否存在负环
        3.直到队空
     * */

    int dis[N];//最短路
    bool vis[N]; //判断是否在队列里
    int in[N]; //入队次数
    queue<int> q;

    bool spfa(int st) {
    	//init
        memset(vis, false, sizeof(vis));
        vis[st] = true;
        memset(dis, -1, sizeof(dis));//最长路初始化
        dis[st] = 0;
        memset(in, 0, sizeof(in));
        in[st] = 1;

        q.push(st);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = false;
            
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].to;
                int w = e[i].w;
                
                if (dis[v] < dis[u] + w) { //这里是求最长路  //求最短路 dis[v]>dis[u]+w
                    dis[v] = dis[u] + w;
                    
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = true;
                        ++in[v];
                        
                        if (in[v] > n + 1) { //判断环 因为加了一个超级源点 故应跟 n + 1 而不是 n 比较
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }
}
using namespace SPFA;

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m; //n个自变量 m个不等式
    for (int i = 1, u, v, w; i <= m; ++i) {
        cin >> u >> v >> w;
        add(u, v, -w);
    }
    for (int i = 1; i <= n; ++i) {
        add(0, i, 0); // 加入一个超级源点
    }
    if (!spfa(0)) {
        cout << "NO" << endl;
    } else {
        for (int i = 1; i <= n; ++i) {
            cout << dis[i] << ' ';
        }
    }
    return 0;
}

你可能感兴趣的:(洛谷,板子)