SPFA

最短路:保证数据不存在负环的情况下,求1-n最短路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct edge {
int v, w;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<edge>> e(n + 1);
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[u].push_back({v, w});
}
vector<int> dis(n + 1, inf);
auto spfa = [&]() {
vector<bool> st(n + 1);
queue<int> q;
q.push(1);
st[1] = true;
dis[1] = 0;
while (q.size()) {
auto u = q.front();
q.pop();
st[u] = 0;
for (auto [v, w] : e[u]) {
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
if (st[v] == 0) {
q.push(v);
st[v] = true;
}
}
}
}
if (dis[n] == inf)
return false;
return true;
};
for (int i = 1; i <= n; i++) deb(i, dis[i]);
if (spfa())
cout << dis[n] << endl;
else
cout << "impossible" << endl;
}

判断全局图中是不是存在负环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct edge {
int v, w;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<edge>> e(n + 1);
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[u].push_back({v, w});
}
vector<int> dis(n + 1, inf);
vector<int> cnt(n + 1);
auto judgefu = [&]() {
vector<bool> st(n + 1, 1);
queue<int> q;
for (int i = 1; i <= n; i++) q.push(i);
while (q.size()) {
auto u = q.front();
q.pop();
st[u] = 0;
for (auto [v, w] : e[u]) {
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if (cnt[v] >= n)
return true;
if (st[v] == 0) {
q.push(v);
st[v] = true;
}
}
}
}
return false;
};
if (judgefu())
cout << "Yes" << endl;
else
cout << "No" << endl;
}

从顶点 1出发能到达的负环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
struct edge {
int v, w;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<edge>> e(n + 1);
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[u].push_back({v, w});
if (w >= 0)
e[v].push_back({u, w});
}
vector<int> dis(n + 1, inf);
vector<int> cnt(n + 1);
auto judgefu = [&]() {
vector<bool> st(n + 1, 0);
queue<int> q;
q.push(1);
st[1] = 1;
dis[1] = 0;
while (q.size()) {
auto u = q.front();
q.pop();
st[u] = 0;
for (auto [v, w] : e[u]) {
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if (cnt[v] >= n)
return true;
if (st[v] == 0) {
q.push(v);
st[v] = true;
}
}
}
}
return false;
};
if (judgefu())
cout << "YES" << endl;
else
cout << "NO" << endl;
}
  • 差分约束

给出一组包含 mm 个不等式,有 nn 个未知数的形如:

{xc1xc1y1xc2xc2y2xcmxcmym\begin{cases} x_{c_1}-x_{c'_1}\leq y_1 \\x_{c_2}-x_{c'_2} \leq y_2 \\ \cdots\\ x_{c_m} - x_{c'_m}\leq y_m\end{cases}

的不等式组,求任意一组满足这个不等式组的解。

第一行为两个正整数 n,mn,m,代表未知数的数量和不等式的数量。

接下来 mm 行,每行包含三个整数 c,c,yc,c',y,代表一个不等式 xcxcyx_c-x_{c'}\leq y

一行,nn 个数,表示 x1,x2xnx_1 , x_2 \cdots x_n 的一组可行解,如果有多组解,请输出任意一组,无解请输出 NO

一、 求不等式组的可行解

!!!源点需要满足的条件:从源点出发,一定可以走到所有的边。!!!

步骤
1. 先将每个不等式 xixj+ckx_i \le x_j + c_k,转化成一条从 xjx_j 走到 xix_i,长度为 ckc_k 的边。
2. 找到一个超级源点,使得该源点一定可以遍历到所有边
3. 从源点求一遍单源最短路

结果1:如果存在负环,则原不等式组一定无解
结果2:如果没有负环,则 dist[i]dist[i] 就是原不等式组的一个可行解


二、 如何求最大值或者最小值,这里的最值指的是每个变量的最值(独立但可以同时取到)

结论:如果求的是最小值,则应该求最长路;如果求的是最大值,则应该求最短路

问题:如何转化 xicx_i \le c,其中 cc 是一个常数,这类的不等式。

方法:建立一个超级源点0,然后建立 0 -> i 的边,长度是 cc 即可。

以求 xix_i最大值为例:

求所有从 xix_i 出发,构成的多个形如如下的不等式链

xixj+c1xk+c2+c1x0+c1+c2++cm(x0=0)x_i \le x_j +c_1 \le x_k + c_2 + c_1 \le ··· \le x_0 + c_1 + c_2 + ··· + c_m \qquad(x_0 = 0)

所计算出的上界,最终 xix_i 的最大值等于所有上界的最小值
这里所有上界的最小值可以理解这么一个例子:QQ截图20210225172450.png

把上述转换成图论的问题,就是求 dist[i]dist[i]最小值,即最短路求解

  • xix_i最小值 时则完全相反,求一个形如如下不等式链所计算出的下界,最终在所有下界里取最大值

xixj+c1xk+c2+c1x0+c1+c2++cm(x0=0)x_i \ge x_j +c_1 \ge x_k + c_2 + c_1 \ge ··· \ge x_0 + c_1 + c_2 + ··· + c_m \qquad(x_0 = 0)

转换成图论的问题,就是求 dist[i]dist[i]最大值,即最长路求解

建边技巧:

  • 如何转化 xicx_i \le c,其中 cc 是一个常数,这类的不等式。

    方法:建立一个超级源点0,然后建立 0 -> i 的边,长度是 cc 即可。

  • xaxbcxbxacaddedge(a,b,c)x_a-x_b \ge c \to x_b \le x_a-c \to addedge(a,b,-c)

  • xaxbcaddedge(b,a,c)x_a-x_b \le c \to addedge(b,a,c)

  • x_a=x_b \to addedge(a,b,0) \and addedge(b,a,0)