差分约束系统

差分约束

概念
如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj\(\leqslant\)k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解方法
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
观察ai-aj<=w,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量ai为结点,对于约束条件ai-aj<=w,连接一条边(j,i),边权为w。(这为最短路),同样最长路 ai - aj >= w,连接一条边(j,i),边权为-w.

判断所求最长路or最短路:
x \(\leqslant\) 3
x \(\leqslant\) 5
x \(\leqslant\) -1
那么这个不等式的解应为 :x \(\leqslant\) -1
是为最短路,反之不等式同大取大,为最长路。
注意
1.不等式组转化成的图可能含有负权边,即可能会有负环,所以要用SPFA算法判环。
2.需要引入虚拟源点(超级源点),共n+1个点。
3.最短路判负环,最长路判正环 。SPFA算法中需要加一个num[]数组来记录当前访问过的结点数,如果在更新完某条边后结点数大于等于n,则说明有环. 无解就是有环的情况。
4.建立一个虚拟(又称超级)源点,以虚拟源点对所有点连一条边权为0的点,保证图的连通。
例题一

luogu P1260 工程规划

分析
读懂题意我们发现就是一个差分约束系统,至于非负整数解,求出答案中的最小值,把每个答案减去这个最小值即可。
输入

5 8
1 2 0
1 5 -1
2 5 1
3 1 5
4 1 4
4 3 -1
5 3 -1
5 4 -3

输出

0
2
5
4
1

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <limits.h>
using namespace std;
typedef long long ll;
const int N=2e6;
struct  edge{
    int from;
    int to;
    int dis;
    int next;
}e[N];
int head[N],cnt;
void add(int u,int v,int w)
{
     ++cnt;
     e[cnt].from = u;
     e[cnt].to = v;
     e[cnt].dis = w;
     e[cnt].next = head[u];
     head[u] = cnt;
}
int mapp[N];
int num[N];
bool vis[N];
int n,m,u,v,w;
queue<int> q;
void SPFA(int u)
{
    int minn = INT_MAX;
    memset(mapp,0x3f,sizeof(mapp));
    memset(vis,0,sizeof(vis));
    mapp[u] = 0; vis[u] = true;
    q.push(u);
    while(!q.empty())
    {
       int x = q.front();
       vis[x] = false;
       q.pop();
       for(int i=head[x];i;i=e[i].next)
       {
           int y = e[i].to;
           if(mapp[y] > mapp[x] + e[i].dis)
           {
               mapp[y] = mapp[x] + e[i].dis;
               if(vis[y] == false)
               {
                  vis[y] = true;
                  num[y]++;
                  q.push(y);
               }
               if(num[y] >= n) 
               {
                   puts("NO SOLUTION");
                   return;
               }
           }
       }
    }
    for(int i=1;i<=n;i++) minn = min(minn,mapp[i]);
    for(int i=1;i<=n;i++) printf("%d\n",mapp[i]-minn);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(v,u,w);
    }
    for(int i=1;i<=n;i++) add(n+1,i,0);
    SPFA(n+1);
    return 0;
}
上一篇:「HTML+CSS」--自定义加载动画【012】


下一篇:ThinkPHP中initialize和construct的不同