差分约束系统
百度百科 :如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
求解
下面就是一个差分约束系统。
{ x 1 − x 2 < = c 1 x 2 − x 3 < = c 2 x 1 − x 3 < = c 3 \begin{cases} x_1-x_2<=c_1\\\\ x_2-x_3<=c_2\\\\ x_1-x_3<=c_3 \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x1−x2<=c1x2−x3<=c2x1−x3<=c3
每一项约束条件都和最短路中的三角不等式 d i s [ v ] − d i s [ u ] < = w dis[v]-dis[u]<=w dis[v]−dis[u]<=w有相似之处。利用这一点我们可以把它转化为一个图论问题。也就是说,建出一张有向图,它的每个顶点都对应差分约束系统中的一个未知量,源点到每个顶点的最短路对应这些未知量的值,而每条边对应一个约束条件。最短路平衡(无法再进行松弛操作)之后, d i s [ i ] dis[i] dis[i]就是差分约束系统的一组解。 而如果在图中出现了负环,也就对应了 x 1 < x 2 < x 3 < x 4 x_1<x_2<x_3<x_4 x1<x2<x3<x4的无解情况。
因为有最后建立的图不连通的情况,因此,最后再建立一个超级源点X,从它向所有顶点连一条边权为W的边。这样,从X跑完最短路之后, d i s [ i ] dis[i] dis[i]就是一组解。并且该解是“最大解”,即每个变量x都取能取到的最大且小于等于W的值。(求最大值)
如果要求每个变量x都大于等于W (求最小值),S只需按照最长路的三角不等式( d i s [ v ] − d i s [ u ] > = w dis[v]-dis[u]>=w dis[v]−dis[u]>=w)进行变更。
用最短路求解差分约束系统,是把某一个变量(即图中的源点)假设为一个值X,以此来推出其他变量可取的最大值。同理,用最长路求解差分约束系统,是把某一个变量(即图中的源点)假设为一个值X,以此来推出其他变量可取的最小值。
代码
#include <bits/stdc++.h>
using namespace std;
const int M = 1e4 + 5;
const int N = M;
int head[N];
int ct = 1;
struct node
{
int next, v, w;
} edge[M];
void add(int u, int v, int w)
{
edge[ct].v = v;
edge[ct].w = w;
edge[ct].next = head[u];
head[u] = ct++;
}
bool vis[N];
int cnt[N];
int dis[N];
int n, m;
bool spfa(int beg)
{
memset(dis, 0x3f3f3f3f, sizeof dis);
//memset(dis, -0x3f3f3f3f, sizeof dis);
dis[beg] = 0;
queue<int> q;
q.push(beg);
vis[beg] = 1;
while (q.size())
{
int now = q.front();
vis[now] = 0;
q.pop();
for (int i = head[now]; i; i = edge[i].next)
{
int v = edge[i].v;
int w = edge[i].w;
if (dis[now] + w < dis[v])
// if (dis[now] + w > dis[v])
{
dis[v] = dis[now] + w;
cnt[v]++;
if (cnt[v] > n) //多了一个超级源点,因此是>=n+1
return false;
vis[v] = 1;
q.push(v);
}
}
}
return true;
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
add(v, u, w);
//add(u, v, -w);
}
for (int i = 1; i <= n; i++)
add(0, i, 0);
if (spfa(0))
{
for (int i = 1; i <= n; i++)
printf("%d%c", dis[i], i == n ? '\n' : ' ');
}
else
puts("NO");
return 0;
}