差分约束模板

Posted hesorchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了差分约束模板相关的知识,希望对你有一定的参考价值。

差分约束系统

百度百科 :如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。

求解

洛谷模板题

下面就是一个差分约束系统。

{ x 1 − x 2 < = c 1 x 2 − x 3 < = c 2 x 1 − x 3 < = c 3 \\begin{cases} x_1-x_2<=c_1\\\\\\\\ x_2-x_3<=c_2\\\\\\\\ x_1-x_3<=c_3 \\end{cases} x1x2<=c1x2x3<=c2x1x3<=c3

每一项约束条件都和最短路中的三角不等式 d i s [ v ] − d i s [ u ] < = w dis[v]-dis[u]<=w dis[v]dis[u]<=w有相似之处。利用这一点我们可以把它转化为一个图论问题。也就是说,建出一张有向图,它的每个顶点都对应差分约束系统中的一个未知量,源点到每个顶点的最短路对应这些未知量的值,而每条边对应一个约束条件。最短路平衡(无法再进行松弛操作)之后, d i s [ i ] dis[i] dis[i]就是差分约束系统的一组解。 而如果在图中出现了负环,也就对应了 x 1 < x 2 < x 3 < x 4 x_1<x_2<x_3<x_4 x1<x2<x3<x4的无解情况。

因为有最后建立的图不连通的情况,因此,最后再建立一个超级源点X,从它向所有顶点连一条边权为W的边。这样,从X跑完最短路之后, d i s [ i ] dis[i] dis[i]就是一组解。并且该解是“最大解”,即每个变量x都取能取到的最大且小于等于W的值。(求最大值)

如果要求每个变量x都大于等于W (求最小值),只需按照最长路的三角不等式( d i s [ v ] − d i s [ u ] > = w dis[v]-dis[u]>=w dis[v]dis[u]>=w)进行变更。

代码

#include <bits/stdc++.h>
using namespace std;

const int M = 1e4 + 5;
const int N = M;
int head[N];
int ct = 1;

struct node
{
    int next, v, w;
} edge[M];

void add(int u, int v, int w)
{
    edge[ct].v = v;
    edge[ct].w = w;
    edge[ct].next = head[u];
    head[u] = ct++;
}

bool vis[N];
int cnt[N];
int dis[N];
int n, m;
bool spfa(int beg)
{
    memset(dis, 0x3f3f3f3f, sizeof dis);
    //memset(dis, -0x3f3f3f3f, sizeof dis);
    dis[beg] = 0;
    queue<int> q;
    q.push(beg);
    vis[beg] = 1;
    while (q.size())
    {
        int now = q.front();
        vis[now] = 0;
        q.pop();
        for (int i = head[now]; i; i = edge[i].next)
        {
            int v = edge[i].v;
            int w = edge[i].w;
            if (dis[now] + w < dis[v])
            //  if (dis[now] + w > dis[v])
            {
                dis[v] = dis[now] + w;
                cnt[v]++;
                if (cnt[v] > n) //多了一个超级源点,因此是>=n+1
                    return false;
                vis[v] = 1;
                q.push(v);
            }
        }
    }
    return true;
}
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        add(v, u, w);
        //add(u, v, -w);
    }
    for (int i = 1; i <= n; i++)
        add(0, i, 0);
    if (spfa(0))
    {
        for (int i = 1; i <= n; i++)
            printf("%d%c", dis[i], i == n ? '\\n' : ' ');
    }
    else
        puts("NO");
    return 0;
}

参考资料

  1. 算法学习笔记(11): 差分约束

  2. 【题解】洛谷 P5960 【模板】差分约束算法

以上是关于差分约束模板的主要内容,如果未能解决你的问题,请参考以下文章

浅谈差分约束系统

poj1201差分约束模板题

Luogu P5960 模板差分约束算法

[poj3159]Candies(差分约束+链式前向星dijkstra模板)

洛谷 P5960 模板差分约束算法(spfa)

浅谈差分约束系统