[模板][最短路] 差分约束系统
差分约束系统
定义 来自某度百科
如果一个系统由 \(n\) 个变量和 \(m\) 个约束条件组成,形成 \(m\) 个形如 \(a_i-a_j≤k\) 的不等式 (\(i,j\in[1,n]\), \(k\)为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
简单来说,给你一组不等式:
\[\left \{ \begin{array}{}
x_1 - x_2 \le c_1 \\
x_2 - x_3 \le c_2 \\
\ \ \ \ \ \cdots \\
x_i - x_j \le c_k
\end{array}\right.
\]
求这组不等式的可行解。
解法
该问题等价于在一个有向图上求最短/长路,
首先建立一个超级源点, 向每个节点链接一条边权为 \(0\) 的边,
然后开始转化:
-
转化为最短路
\[\]x_1 \le c_1 + x_2\
x_2 \le c_2 + x_3\
\ \ \ \ \ \cdots \
x_i \le c_k + x_j
\end{array}\right.\[\] -
转化为最长路
\[\]x_1 \ge -c_1 + x_2\
x_2 \ge -c_2 + x_3\
\ \ \ \ \ \cdots \
x_i \ge -c_k + x_j
\end{array}\right.\[\]
我们要做的就是从大于号左边(小于号右边)向大于号右边(小于号左边)连接一条边权为 \(c_i\)(\(-c_i\)) 的边,跑最短路即可。
无解?
联系最短路,发现当图上有负环无解,
所以跑 \(SPFA\) 即可。
模板
// 注释掉的是跑最长路找最小正数可行解的方法
# include <iostream>
# include <cstdio>
# include <queue>
# include <cstring>
# define MAXN 5005
# define MAXM 10005
using namespace std;
struct edge{
int u, v, next, w;
}e[MAXM];
int hd[MAXM], cntE;
void AddE(int u, int v, int w){
e[++cntE].u = u, e[cntE].v = v, e[cntE].next = hd[u], hd[u] = cntE;
e[cntE].w = w;
}
//
int dis[MAXN], cntQ[MAXN];
bool inQ[MAXN];
bool SPFA(int from, int lim){
queue<int>Q;
memset(inQ, 0, sizeof(inQ));
memset(dis, 0x3f, sizeof(dis));
// for(int i = 1; i <= lim-1; i++){
// dis[i] = -100000;
// }
dis[from] = 0;
Q.push(from); inQ[from] = 1;
while(!Q.empty()){
int now = Q.front(); Q.pop();
inQ[now] = 0;
for(int i = hd[now]; i; i = e[i].next){
if(dis[e[i].v] > dis[now] + e[i].w){
// if(dis[e[i].v] < dis[now] + e[i].w){
dis[e[i].v] = dis[now] + e[i].w;
if(!inQ[e[i].v]){
Q.push(e[i].v); inQ[e[i].v] = 1;
cntQ[e[i].v] += 1;
if(cntQ[e[i].v] == lim){
return false;
}
}
}
}
}
return true;
}
//
int main(){
int n, m;
cin>>n>>m;
for(int i = 1, u, v, w; i <= m; i++){
cin>>u>>v>>w;
AddE(v, u, w); // 注意这里的连边方向,应该是从大于连向小于
// AddE(u, v, -w);
}
for(int i = 1; i <= n; i++){
AddE(0, i, 0);
} // 建立超级源点
bool chk = SPFA(0, n+1);
if(chk){
for(int i = 1; i <= n; i++){
cout<<dis[i]<<' ';
}
}
else{
cout<<"NO";
}
return 0;
}