Difference Constraints

Definition

A system of difference constraints is a special system of nn linear inequalities in n variables. It contains nn variables x1,x2,…,xnx_1,x_2,\dots,x_n and mm constraints. Each constraint is formed by taking the difference of two variables, in the form xi−xj≤ckx_i-x_j\leq c_k, where 1≤i,j≤n,i≠j,1≤k≤m1 \leq i, j \leq n, i \neq j, 1 \leq k \leq m, and ckc_k is a constant (which may be nonnegative or negative). The problem is to find a solution x1=a1,x2=a2,…,xn=anx_1=a_1,x_2=a_2,\dots,x_n=a_n satisfying all constraints, or determine that no solution exists.

Every constraint xi−xj≤ckx_i-x_j\leq c_k in a system of difference constraints can be transformed into xi≤xj+ckx_i\leq x_j+c_k, which is very similar to the triangle inequality dist[y]≤dist[x]+zdist[y]\leq dist[x]+z in the single-source shortest-path problem. Therefore, we can regard each variable xix_i as a vertex in a graph and, for each constraint xi−xj≤ckx_i-x_j\leq c_k, add a directed edge of length ckc_k from vertex jj to vertex ii.

Note that if {a1,a2,…,an}\{a_1,a_2,\dots,a_n\} is a solution to the system of difference constraints, then for any constant dd, {a1+d,a2+d,…,an+d}\{a_1+d,a_2+d,\dots,a_n+d\} is clearly also a solution, because dd cancels out when taking differences.

Process

Set dist[0]=0dist[0]=0 and add an edge of weight 00 from vertex 0 to every other vertex. Run a single-source shortest-path algorithm. If the graph contains a negative cycle, the given system of difference constraints has no solution; otherwise, xi=dist[i]x_i=dist[i] is a solution to the system.

Properties

Bellman–Ford or its queue-optimized version (commonly called SPFA, which can be very fast on some random graphs) is generally used to determine whether the graph contains a negative cycle. Its worst-case time complexity is O(nm)O(nm).

Common transformation techniques

Example Luogu P1993 Little K's Farm

Problem statement: solve a system of difference constraints with mm constraints, each of which has the form xa−xb≥ckx_a-x_b\geq c_k, xa−xb≤ckx_a-x_b\leq c_k, or xa=xbx_a=x_b, and determine whether the system has a solution.

MeaningTransformationAdded edge
xa−xb≥cx_a - x_b \geq cxb−xa≤−cx_b - x_a \leq -cadd(a, b, -c);
xa−xb≤cx_a - x_b \leq cxa−xb≤cx_a - x_b \leq cadd(b, a, c);
xa=xbx_a = x_bxa−xb≤0, xb−xa≤0x_a - x_b \leq 0, \space x_b - x_a \leq 0add(b, a, 0), add(a, b, 0);

Run the negative-cycle check. If there is no negative cycle, output Yes; otherwise, output No.

Reference code
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

struct edge {
  int v, w, next;
} e[40005];

int head[10005], vis[10005], tot[10005], cnt;
long long ans, dist[10005];
queue<int> q;

void addedge(int u, int v, int w) {  // add an edge
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].next = head[u];
  head[u] = cnt;
}

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  int n, m;
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int op, x, y, z;
    cin >> op;
    if (op == 1) {
      cin >> x >> y >> z;
      addedge(y, x, z);
    } else if (op == 2) {
      cin >> x >> y >> z;
      addedge(x, y, -z);
    } else {
      cin >> x >> y;
      addedge(x, y, 0);
      addedge(y, x, 0);
    }
  }
  for (int i = 1; i <= n; i++) addedge(0, i, 0);
  memset(dist, -0x3f, sizeof(dist));
  dist[0] = 0;
  vis[0] = 1;
  q.push(0);
  while (!q.empty()) {  // detect negative cycles; see above
    int cur = q.front();
    q.pop();
    vis[cur] = 0;
    for (int i = head[cur]; i; i = e[i].next)
      if (dist[cur] + e[i].w > dist[e[i].v]) {
        dist[e[i].v] = dist[cur] + e[i].w;
        if (!vis[e[i].v]) {
          vis[e[i].v] = 1;
          q.push(e[i].v);
          tot[e[i].v]++;
          if (tot[e[i].v] >= n) {
            cout << "No\n";
            return 0;
          }
        }
      }
  }
  cout << "Yes\n";
  return 0;
}

Example P4926 [1007] Multiplier

Ignoring binary search and other details, here we discuss only how to solve the difference system xixj≤ck\frac{x_i}{x_j}\leq c_k.

Taking a log⁡\log of each xi,xjx_i,x_j and ckc_k turns multiplication into addition: log⁡xi−log⁡xj≤log⁡ck\log x_i-\log x_j \leq \log c_k, which can then be solved using difference constraints.

Bellman–Ford implementation for detecting negative cycles

The following is an implementation of the Bellman–Ford algorithm for determining whether a graph contains a negative cycle. Before calling it, ensure that the graph is connected.

Implementation

C++

bool Bellman_Ford() {
  for (int i = 0; i < n; i++) {
    bool jud = false;
    for (int j = 1; j <= n; j++)
      for (int k = h[j]; ~k; k = nxt[k])
        if (dist[j] > dist[p[k]] + w[k])
          dist[j] = dist[p[k]] + w[k], jud = true;
    if (!jud) break;
  }
  for (int i = 1; i <= n; i++)
    for (int j = h[i]; ~j; j = nxt[j])
      if (dist[i] > dist[p[j]] + w[j]) return false;
  return true;
}

Python

def Bellman_Ford():
    for i in range(0, n):
        jud = False
        for j in range(1, n + 1):
            while ~k:
                k = h[j]
                if dist[j] > dist[p[k]] + w[k]:
                    dist[j] = dist[p[k]] + w[k]
                    jud = True
                k = nxt[k]
        if jud == False:
            break
    for i in range(1, n + 1):
        while ~j:
            j = h[i]
            if dist[i] > dist[p[j]] + w[j]:
                return False
            j = nxt[j]
    return True

Exercises

Usaco2006 Dec Wormholes

SCOI2011 Candy

POJ 1364 King

POJ 2983 Is the Information Reliable?

This content comes from OI Wiki: Difference Constraints. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.