差分约束
浅谈差分约束
差分约束系统是一种特殊的 \(n\) 元一次不等式组,它包含 \(n\) 个变量 $x_1,x_2,...,x_n $ 以及 \(m\) 个约束条件,每个约束条件是由两个其中的变量做差构成的
形如 \(x_i−x_j≤c_k\) , 其中 \(1≤i,j≤n,i≠j,1≤k≤m\) 并且 \(c_k\) 是常数,约束条件可以变形 \(x_i−x_j≤c_k⇔x_i≤x_j+c_k\)
这就很像图论中的求最短路不等式 \(dist[y]≤dist[x]+z\)
因此,我们可以把每个变量 \(x_i\) 看做图中的一个结点,对于每个约束条件 \(x_i−x_j≤c_k\) ,看作从结点 \(j\) 向结点 \(i\) 连一条长度为 \(c_k\) 的有向边。
那么差分约束可以用来解决什么问题呢?
首先就是可以求解不等式组,用 SPFA
判负环,如果存在负环,说明原方程无解,如果不存在负环,输出 dist[i]
即可。
同时也可解决最值问题。
如果求的是最小值,则应该求最长路中的最小 dist
;如果求的是最大值,则应该求最短路中的最大 dist
;
可以发现,直接跑最长路最短路,SPFA
判负环这些操作非常的简单,所以差分约束的难点在哪里?
如何建图。
只需要依据不等式关系建图:
add(b, a, 0), add(a, b, 0);//A=B
add(a, b, 1);//B≥A+1
add(b, a, 0);//A≥B
add(b, a, 1);//A≥B+1
add(a, b, 0);//B≥A
接下来我们进行实战:
P3275 [SCOI2011]糖果
这个题 SPFA
是过不了的,无法通过 hack
数据,但是赛时官方数据还是可以搞过去的。
#define rint register int
#define endl '\n'
using namespace std;
const int N = 1e5 + 5;
const int M = 3e5 + 5;
int n, m;
int idx, h[N], ne[M], e[M], w[M];
long long dist[N];
deque<int> q;
bool v[N];
int cnt[N];
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
bool SPFA()
{
memset(dist,-0x3f,sizeof dist);
memset(v,0,sizeof v);
q.push_back(0);
v[0] = true;
dist[0] = 0;
cnt[0] = 0;
while (!q.empty())
{
int x = q.back();
q.pop_back();
v[x] = false;
for (rint i = h[x]; i; i = ne[i])
{
int y = e[i];
int z = w[i];
if (dist[y] < dist[x] + z)
{
dist[y] = dist[x] + z;
cnt[y] = cnt[x] + 1;
if (cnt[y] >= n + 1)
return true;
if (!v[y])
{
q.push_back(y);
v[y] = true;
}
}
}
}
return false;
}
signed main()
{
scanf("%d %d", &n, &m);
for (rint i = 1; i <= m; i++)
{
int op, a, b;
scanf("%d %d %d", &op, &a, &b);
if (op == 1)
{
add(b, a, 0);
add(a, b, 0);
}
if (op == 2)
add(a, b, 1);
if (op == 3)
add(b, a, 0);
if (op == 4)
add(b, a, 1);
if (op == 5)
add(a, b, 0);
}
for (rint i = 1; i <= n; i++)
add(0, i, 1);
if (SPFA() == true)
{
cout << "-1" << endl;
return 0;
}
long long ans = 0;
for (rint i = 1; i <= n; i++)
{
ans = ans + dist[i];
}
printf("%lld\n", ans);
return 0;
}
UVA1723 区间
#include <bits/stdc++.h>
#define rint register int
#define endl '\n'
using namespace std;
const int N = 1e6 + 5;
const int M = 2e6 + 5;
int n, cnt, m, s;
int idx, h[N], ne[M], e[M], w[M], dist[N];
deque<int> q;
bool v[N];
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
void SPFA(int s)
{
memset(dist, -0x3f, sizeof dist);
memset(v, 0, sizeof v);
q.push_back(s);
v[s] = true;
dist[s] = 0;
while (!q.empty())
{
int x = q.back();
q.pop_back();
v[x] = false;
for (rint i = h[x]; i; i = ne[i])
{
int y = e[i];
int z = w[i];
if (dist[y] < dist[x] + z)
{
dist[y] = dist[x] + z;
if (!v[y])
{
q.push_back(y);
v[y] = true;
}
}
}
}
}
signed main()
{
int T;
cin >> T;
while (T--)
{
memset(h,0,sizeof h);
memset(e,0,sizeof e);
memset(ne,0,sizeof ne);
memset(w,0,sizeof w);
idx = 0;
cin >> n;
int minn = 0x3f3f3f3f;
int maxx = -1;
for (rint i = 1; i <= n; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b + 1, c);
maxx = max(b + 1 , maxx);
minn = min(a , minn);
}
for (rint i = minn; i <= maxx; i++)
{
add(i - 1, i, 0);
add(i, i - 1, -1);
}
SPFA(minn);
cout << dist[maxx] << endl;
if(T){
cout<<endl;
}
}
return 0;
}