P5960 【模板】差分约束
差分约束问题的典型特征是一组不等式。只要画出约束图,这类问题都可以准换为最短路径问题。注意:约束图是有向图。
约束图的顶点( V V V) = 一个未知数对应一个顶点( v 1 , v 2 , . . . , v n v_1, v_2, ...,v_n v1,v2,...,vn) + 一个额外的顶点( v 0 v_0 v0)
约束图的边由两部分组成:
举例:
原不等式组:
{ x 1 − x 2 ≤ 3 x 2 − x 3 ≤ − 2 x 1 − x 3 ≤ 1 \begin{cases} x_1 - x_2 \leq 3 \\ x_2 - x_3 \leq -2 \\ x_1 - x_3 \leq 1 \end{cases} ⎩ ⎨ ⎧x1−x2≤3x2−x3≤−2x1−x3≤1
引入额外顶点 v 0 v_0 v0,并画权值为0的有向边:
变形:
{ x 1 ≤ x 2 + 3 x 2 ≤ x 3 − 2 x 1 ≤ x 3 + 1 \begin{cases} x_1 \leq x_2 + 3 \\ x_2 \leq x_3 -2 \\ x_1 \leq x_3 + 1 \end{cases} ⎩ ⎨ ⎧x1≤x2+3x2≤x3−2x1≤x3+1
得到最终约束图:
上文提到,差分约束问题可以用最短路径求解,所以,我们也用一个数组 d[]
记录最短路径。我们令 v 0 v_0 v0 为起点,并初始化为 0。这里,就是 d [ 0 ] = 0 d[0] = 0 d[0]=0。接着,用最短路径算法求 v 0 v_0 v0 到各点的最短路径。各点的最短路径就是不等式组的一个解,即 x = ( x 1 , x 2 , . . . , x n ) = ( d [ 1 ] , d [ 2 ] , . . . , d [ n ] ) x = (x_1, x_2, ..., x_n) = (d[1], d[2], ..., d[n]) x=(x1,x2,...,xn)=(d[1],d[2],...,d[n])。
有两点需要注意:
#include
using namespace std;
#define MAX 5003
#define INF 1e8
typedef struct
{
int from, to, worth;
} edge;
int n, m;
vector<edge> E;
int d[MAX];
int quickin(void)
{
int ret = 0;
bool flag = false;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
flag = true;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
ret = 10 * ret + ch - '0';
ch = getchar();
}
if (flag)
ret = -ret;
return ret;
}
// Bellman-Ford
bool solve(void)
{
fill(begin(d), end(d), INF);
d[0] = 0;
for (int i = 0; i < n; i++)
{
bool flag = false;
for (int j = 0; j < E.size(); j++)
{
edge e = E[j];
if (d[e.from] != INF && d[e.to] > d[e.from] + e.worth)
{
d[e.to] = d[e.from] + e.worth;
flag = true;
if (i == n - 1)
return true; // 存在负环
}
}
if (!flag)
break;
}
return false;
}
int main()
{
n = quickin(), m = quickin();
for (int i = 0; i < m; i++)
{
int a, b, c;
a = quickin(), b = quickin(), c = quickin();
E.push_back(edge{b, a, c});
}
// 添加额外点
for (int i = 1; i <= n; i++)
E.push_back(edge{0, i, 0});
if (solve())
puts("NO");
else
for (int i = 1; i <= n; i++)
printf("%d ", d[i]);
return 0;
}
完