差分约束

浅谈差分约束

差分约束系统是一种特殊的 \(n\) 元一次不等式组,它包含 \(n\) 个变量 $x_1,x_2,...,x_n $ 以及 \(m\) 个约束条件,每个约束条件是由两个其中的变量做差构成的

形如 \(x_i−x_j≤c_k\) , 其中 \(1≤i,j≤n,i≠j,1≤k≤m\) 并且 \(c_k\) 是常数,约束条件可以变形 \(x_i−x_j≤c_k⇔x_i≤x_j+c_k\)

这就很像图论中的求最短路不等式 \(dist[y]≤dist[x]+z\)

因此,我们可以把每个变量 \(x_i\) 看做图中的一个结点,对于每个约束条件 \(x_i−x_j≤c_k\) ,看作从结点 \(j\) 向结点 \(i\) 连一条长度为 \(c_k\) 的有向边。

那么差分约束可以用来解决什么问题呢?

首先就是可以求解不等式组,用 SPFA 判负环,如果存在负环,说明原方程无解,如果不存在负环,输出 dist[i] 即可。

同时也可解决最值问题。

如果求的是最小值,则应该求最长路中的最小 dist ;如果求的是最大值,则应该求最短路中的最大 dist

可以发现,直接跑最长路最短路,SPFA 判负环这些操作非常的简单,所以差分约束的难点在哪里?

如何建图。

只需要依据不等式关系建图:

add(b, a, 0), add(a, b, 0);//A=B
add(a, b, 1);//B≥A+1
add(b, a, 0);//A≥B
add(b, a, 1);//A≥B+1
add(a, b, 0);//B≥A

接下来我们进行实战:

P3275 [SCOI2011]糖果

这个题 SPFA 是过不了的,无法通过 hack 数据,但是赛时官方数据还是可以搞过去的。


#define rint register int
#define endl '\n'

using namespace std;

const int N = 1e5 + 5;
const int M = 3e5 + 5;

int n, m;
int idx, h[N], ne[M], e[M], w[M];
long long dist[N];

deque<int> q;

bool v[N];
int cnt[N];

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

bool SPFA()
{
    memset(dist,-0x3f,sizeof dist);
    memset(v,0,sizeof v);
    
    q.push_back(0);
    v[0] = true;
    dist[0] = 0;
    cnt[0] = 0;
    
    while (!q.empty())
    {
        int x = q.back();
        q.pop_back();
        v[x] = false;
        for (rint i = h[x]; i; i = ne[i])
        {
            int y = e[i];
            int z = w[i];
            if (dist[y] < dist[x] + z)
            {
                dist[y] = dist[x] + z;
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n + 1)
                    return true;
                if (!v[y])
                {
                    q.push_back(y);
                    v[y] = true;
                }
            }
        }
    }
    
    return false;
}

signed main()
{
    scanf("%d %d", &n, &m);

    for (rint i = 1; i <= m; i++)
    {
        int op, a, b;
        scanf("%d %d %d", &op, &a, &b);
        if (op == 1)
        {
            add(b, a, 0);
            add(a, b, 0);
        }

        if (op == 2)
            add(a, b, 1);

        if (op == 3)
            add(b, a, 0);

        if (op == 4)
            add(b, a, 1);

        if (op == 5)
            add(a, b, 0);
    }

    for (rint i = 1; i <= n; i++)
        add(0, i, 1);

    if (SPFA() == true)
    {
        cout << "-1" << endl;
        return 0;
    }

    long long ans = 0;

    for (rint i = 1; i <= n; i++)
    {
        ans = ans + dist[i];
    }

	printf("%lld\n", ans);

    return 0;
}

UVA1723 区间

#include <bits/stdc++.h>

#define rint register int
#define endl '\n'

using namespace std;

const int N = 1e6 + 5;
const int M = 2e6 + 5;

int n, cnt, m, s;
int idx, h[N], ne[M], e[M], w[M], dist[N];

deque<int> q;

bool v[N];

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

void SPFA(int s)
{
    memset(dist, -0x3f, sizeof dist);
    memset(v, 0, sizeof v);

    q.push_back(s);
    v[s] = true;
    dist[s] = 0;

    while (!q.empty())
    {
        int x = q.back();
        q.pop_back();
        v[x] = false;
        for (rint i = h[x]; i; i = ne[i])
        {
            int y = e[i];
            int z = w[i];
            if (dist[y] < dist[x] + z)
            {
                dist[y] = dist[x] + z;
                if (!v[y])
                {
                    q.push_back(y);
                    v[y] = true;
                }
            }
        }
    }
}

signed main()
{
    int T;
    cin >> T;

    while (T--)
    {
        memset(h,0,sizeof h);
        memset(e,0,sizeof e);
        memset(ne,0,sizeof ne);
        memset(w,0,sizeof w);
        idx = 0;
        
        cin >> n;
        
        int minn = 0x3f3f3f3f;
        int maxx = -1;
        
        for (rint i = 1; i <= n; i++)
        {
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b + 1, c);
            maxx = max(b + 1 , maxx);
            minn = min(a , minn);
        }

        for (rint i = minn; i <= maxx; i++)
        {
            add(i - 1, i, 0);
            add(i, i - 1, -1);
        }

        SPFA(minn);

        cout << dist[maxx] << endl;
        
        if(T){
            cout<<endl;
        }
    }

    return 0;
}
posted @ 2022-03-05 16:07  PassName  阅读(46)  评论(0编辑  收藏  举报