「上帝一声不响,一切皆由我定」|

BadBadBad__AK

园龄:1年6个月粉丝:13关注:6

差分约束(Differential constraint)

test

definition

差分约束系统 是一种特殊的 n 元一次不等式组,它包含 n 个变量 x1,x2,,xn 以及 m 个约束条件,每个约束条件是由两个其中的变量做差构成的,形如 xixjck,其中 1i, jn, ij, 1km 并且 ck 是常数(可以是非负数,也可以是负数)。我们要解决的问题是:求一组解 x1=a1,x2=a2,,xn=an,使得所有的约束条件得到满足,否则判断出无解。

差分约束系统中的每个约束条件 xixjck 都可以变形成 xixj+ck,这与单源最短路中的三角形不等式 dist[y]dist[x]+z 非常相似。因此,我们可以把每个变量 xi 看做图中的一个结点,对于每个约束条件 xixjck,从结点 j 向结点 i 连一条长度为 ck 的有向边。

注意到,如果 {a1,a2,,an} 是该差分约束系统的一组解,那么对于任意的常数 d{a1+d,a2+d,,an+d} 显然也是该差分约束系统的一组解,因为这样做差后 d 刚好被消掉。

process

dist[0]=0 并向每一个点连一条权重为 0 边,跑单源最短路,若图中存在负环,则给定的差分约束系统无解,否则,xi=dist[i] 为该差分约束系统的一组解。

properties

一般使用 BellmanFord 或队列优化的 BellmanFord(俗称 SPFA,在某些随机图跑得很快)判断图中是否存在负环,最坏时间复杂度为 O(nm)

in common use

例题 luogu P1993K 的农场
accoders P4615K 的农场

题意 转化 连边
xaxbc xbxa add(a,b,c);
xaxbc xaxbc add(b,a,c);
xa=xb xaxb0, xbxa0 add(b,a,0),add(a,b,0);

跑判断负环,如果不存在负环,输出 Yes,否则输出 No

Reference code
#include <algorithm>
#include <cstdio>
#include <cstring>
#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) {  // 加边
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].next = head[u];
  head[u] = cnt;
}

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= m; i++) {
    int op, x, y, z;
    scanf("%d", &op);
    if (op == 1) {
      scanf("%d%d%d", &x, &y, &z);
      addedge(y, x, z);
    } else if (op == 2) {
      scanf("%d%d%d", &x, &y, &z);
      addedge(x, y, -z);
    } else {
      scanf("%d%d", &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()) {  // 判负环,看上面的
    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) {
            puts("No");
            return 0;
          }
        }
      }
  }
  puts("Yes");
  return 0;
}

BellmanFord 判负环代码实现

下面是用 BellmanFord 算法判断图中是否存在负环的代码实现,请在调用前先保证图是连通的。

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

ppt

image
image
image

本文作者:BadBadBad__AK

本文链接:https://www.cnblogs.com/BadBadBad/p/17948635/DifferentialConstraint

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BadBadBad__AK  阅读(113)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起