对于不等式组:
X1 - X2 <= 0
X1 - X5 <= -1
X2 - X5 <= 1
X3 - X1 <= 5
X4 - X1 <= 4
X4 - X3 <= -1
X5 - X3 <= -3
X5 - X4 <= -3
求出满足情况的x1~x5,这样的问题我们叫做差分约束系统
差分约束系统的解法利用到了单源最短路径问题中的三角形不等式。即对于任何一条边u -> v,都有:
d(v) <= d(u) + w(u, v)
所以利用这个不等式,我们就可以用已知不等式来建图了。
如x5-1>=x1,那么就从x5连一条权值为-1的边到x1上,以此类推,就可以成功建图。
对于题目的问题,我们有不同的方式处理这张图。
最基本的我们需要找一个源点,但是这些被约束的点肯定不可以作为源点,所以我们创建一个0号节点,对所有的点都连一条权值为0的边,跑单源最短路时以这个点为出发点。
做题时可能会遇到不等式中的符号不相同的情况,但我们可以对它们进行适当的转化
方程给出:X[n-1]-X[0]>=T ,可以进行移项转化为: X[0]-X[n-1]<=-T。
方程给出:X[n-1]-X[0]<T, 可以转化为X[n-1]-X[0]<=T-1。
方程给出:X[n-1]-X[0]=T,可以转化为X[n-1]-X[0]<=T&&X[n-1]-X[0]>=T,再利用(1)进行转化即可
1.判定该不等式方程组是否有解
判断是否有负环即可(证明的话可以自己画一张有负权环的图,很容易看出来),跑spfa
bool spfa()
{
int sum = 0;
deque<int> q;
q.push_back(0);
exist[0] = 1;
dist[0] = 0;
while( !q.empty() )
{
int s = q.size();
int x = q.front();
while( s*dist[x] > sum )
{
q.pop_front();
q.push_back(x);
x = q.front();
}
exist[x] = 0;
q.pop_front();
sum -= dist[x];
for(int i = 0; i < g[x].size(); i++)
{
node t = g[x][i];
if( dist[t.num] > dist[x] + t.val )
{
dist[t.num] = dist[x] + t.val;
if( !exist[t.num] )
{
if( q.empty() || q.front() < dist[t.num] ) q.push_back(t.num);
else q.push_front(t.num);
sum += dist[t.num];
count[t.num] ++;
if( count[t.num] == n ) return true;
exist[t.num] = 1;
}
}
}
}
return false;
}
2.求解不等式组
建完图后跑最短路即可
/*
n个人分糖果,有m条限制
每个限制都要求a分到的糖果不能比b少超过c
要求算出1与n分到糖果的最大差异为多少
每条限制就是dis[a]+c>=dis[b],所以连一条从a到b的权值为c的边
跑最短路即可,由于算1与n的最大差值,所以从从1号点开始跑
保证无负环
*/
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
struct Edge {
int to,next,val;
} edge[300005];
int head[30005],cnt = 0;
void add(int u,int v,int val)
{
edge[cnt].to = v;
edge[cnt].val = val;
edge[cnt].next = head[u];
head[u] = cnt++;
}
ll dist[30005],exist[30005];
int s[30005];
int top = 0;
void spfa(int begin,int n) //起始点和点的数量
{
s[top++] = begin;
for (int i = 1; i <= n; i++) //初始化距离
{
exist[i] = 0;
dist[i] = 1e18;
}
dist[begin] = 0; //起点距离为0
exist[begin] = 1; //exist表示在队列里
while( top )
{
int x = s[--top];
exist[x] = 0; //松弛x
for (int i = head[x]; i != -1; i = edge[i].next)
{
Edge t = edge[i];
if( dist[x] + t.val < dist[t.to] )
{
dist[t.to] = dist[x] + t.val;
if( !exist[t.to] ) //被更新且不在队列,即可入队
{
s[top++] = t.to;
exist[t.to] = 1;
}
//如果从起点到t.num有大于等于n条边,那么说明一定有负环
}
}
}
}
int main()
{
//ios::sync_with_stdio(false);
//cin.tie(0);
int n,m;
//cin >> n >> m;
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++)
{
head[i] = -1;
}
for (int i = 1; i <= m; i++)
{
int x,y;
ll v;
scanf("%d%d%lld",&x,&y,&v);
// cin >> x >> y >> v;
add(x,y,v);
}
spfa(1,n);
printf("%lld\n",dist[n]);
return 0;
}