差分约束:
一:求不等式组的可行解源点需要满足的条件,从源点出发一定可以走到所有的边。(为何不是所有的点??因 为若存在点遍历不到,说明他不在所要求解的不等式中,换言之就是遍历不到的点可以 取任意实数)
步骤:
【1】:先将每个不等式x[i] <= x[j] + c[k] 转化为一条从x[j]走到x[i],长度为c[k]的边
【2】:找一个超级源点,使得超级源点可以遍历到所有的边。
【3】:从源点求一遍单源最短路。
结果一:如果存在负环,则不等式一定无解。
结果二:若不等式有解,则dist[i]是不等式的一个可行解(dist[i]表示从源点 0到i的最短距离)
结果一问题:为什么存在负环则不等式一定无解???
{
如图假设存在负环如下:
则该负环转化为不等式为:
x2 <= x1 + c1, x3 <= x2 + c2, x1 <= x3 + c3;
这里以x2为例:由上述不等式得:
x2 <= x1 + c1 <= x3 + c3 + c1 <= x2 + c1 + c2 + c3因为上图是负环
所以边权之和小于0,即c1 + c2 + c3 < 0因此推出结论:
x2 < x2无解:
结论:所以当不等式无解时 <=> 图中存在负环
}
二:如何求最大值,最小值??注意这里的最大/最小值指的是每个变量的最大/最小值。
首先我们先说结论:若求的是最大值用最短路,若求的是最小值则用最长路。
证明:
{
假设这里求的是最短路
这里同样以上图中不等式: x2 <= x1 + c1, x3 <= x2 + c2;
以x2为例得到:x2 <= x1 + c1, x2 <= x3 + c1 + c3
要使x2成立则要求出x2 <= 右边的值得的最大值
为什么是最大值??
假设求出的答案为x2 <= 3, x2 <= 5要使两个式子都成立,则要取x2 <= 5
因此我们要去他的最大值:
所以要得到最大值则求他的最短路,反之要求他的最小值则求他的最长路。
}
这里还需要考虑两个问题
问题一:若是遇到诸如xi <= c的不等式如何计算??
解决方法:建立一个超级源点从超级源点 -> xi建立一条权值为c的边
问题二:如何肯定一条最短路/最长路一定能对应一个不等式呢??
假设的一条最短路:
其中x0是超级源点。
则x3 <= x2 + c2 <= x1 + c1 + c2 <= c1 + c2即这个不等式表示上述最短路,同样应用为xn同理可证每条最短路/最长路可确定一个不等式。
例题:
幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。
但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, 老师需要满足小朋友们的 K 个要求。
幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入格式
输入的第一行是两个整数 N,K。
接下来 K 行,表示分配糖果时需要满足的关系,每行 3 个数字 X,A,B。
- 如果 X=1.表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多。
- 如果 X=2,表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果。
- 如果 X=3,表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。
- 如果 X=4,表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果。
- 如果 X=5,表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。
小朋友编号从 1 到 N。
输出格式
输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1−1。
数据范围
1≤N<105,
1≤K≤105,
1≤X≤5,
1≤A,B≤N,
输入数据完全随机。输入样例:
5 7 1 1 2 2 3 2 4 4 1 3 4 5 5 4 5 2 3 5 4 5 1
输出样例:
11
题解:
题目求得是最小值,所以用最长路
当X == 1, 所代表的不等式为 a <= b && b >= a;
当X == 2,a < b 即 b >= a + 1
当X == 3,a >= b
当X == 4,a > b 即 a >= b + 1
当X == 5, b >= a
将形如不等式a <= b + C(C是常数)从b -> a 建立一条权值为C的边
if (x == 1) add(a, b, 0), add(b, a, 0); else if (x == 2) add(a, b, 1); else if (x == 3) add(b, a, 0); else if (x == 4) add(b, a, 1); else if (x == 5) add(a, b, 0);
#include <stack>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 100010, M = N * 3;//M为边的长度,为何时N * 3??
//首先题目当X == 1给的边是双向边2 * N,
int n, m; //然后要给超级源点到各个点连一条边N,
int cnt[N]; //所以总共为3 * N
bool st[N];
int dist[N];
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
idx ++ ;
}
bool spfa()
{
memset(dist, -0x3f, sizeof dist);
dist[0] = 0;
stack<int> q;//这题用queue会tle所以可以用stack
q.push(0); //一般最短路求负环tle了可以用stack试一下
while (q.size())
{
auto t = q.top();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] < dist[t] + w[i])//最长路
{
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n + 1) return false;//求最长路,若存在正环,说明无解
dist[j] = dist[t] + w[i];
if (!st[j])
{
st[j] = true;
q.push(j);
}
}
}
}
return true;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m -- )
{
int x, a, b;
scanf("%d %d %d", &x, &a, &b);
if (x == 1) add(a, b, 0), add(b, a, 0);
else if (x == 2) add(a, b, 1);
else if (x == 3) add(b, a, 0);
else if (x == 4) add(b, a, 1);
else if (x == 5) add(a, b, 0);
}
for (int i = 1; i <= n; i ++ ) add(0, i, 1);//建立超级源点
long long res = 0;
if (spfa()) for (int i = 1; i <= n; i ++ ) res += dist[i];//将每个小盆友的糖果加起来
else res = -1;
cout << res << endl;
return 0;
}