加载中...

差分约束 利用图论的不等式求变量

求满足多个不等式组的元素

和题目给出上下界 与 题目所求 可以知道最最长路还是最短路
那么
求x[i]最大值
<=>
求所有上界的最小值
<=>
求所有从0→i的路径和的最小值
<=>
最短路求dist[i]

同理 求x[i]最小值
        <=>
    求所有下界的最大值
        <=>
    求所有从0→i的路径和的最大值
        <=>
    最长路求dist[i]

=表示+1
(1)求不等式组的可行解
(2)!!源点需要满足的条件 从源点出发,一定可以走到所有的边!!!
存在负环说明无解 正环有解
1.先找每个不等式 xi<= xj+ck 转化为已调配从xi到xj长度为ck的一条边
2.找一个超级源点 是的该源点一定可以遍历到所有边
3.从源点求一次单源最短路
结果1:入股存在
结果2:

(3) 求最大最小值 一定是求个每个变量的关系
求最小值 应该求最长路
求最大值 应该求最短路
一定有个绝对关系 作为最小值 xi>=0 转化为 xi>=c c是一个常数 xi<=c 方法 建立超级源点 对每个i连接一个长度为c的边
求xi的最大值 就是0到xi的最短路

至少准备多少糖果 就是求最小值 ->秒出满足所有下界求下界->(下界)秒出大于等于号->秒出最长路

不等式所有下界的最大值 a>= b + c 所以下界是c 连接一个b+c 号 满足所有最长路 memset负无穷 ->大于号
https://www.acwing.com/problem/content/1171/
A=B <-> a>=b && b>=a
a<b <-> b>=a+1 a到b连一个权为1的边
a>=b <-> a>=b
a>b <-> a>=b+1
a<=b <-> b>=a
x>=1 <-> xi>=x+1

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, M = 300010;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
LL dist[N];
int q[N], cnt[N];
bool st[N];

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

bool spfa()
{
    int hh = 0, tt = 1;
    memset(dist, -0x3f, sizeof dist);
    dist[0] = 0;
    q[0] = 0;
    st[0] = true;

    while (hh != tt)
    {
        int t = q[ -- tt];
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] < dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n + 1) return false;//因为这里是n+1个点(超级源点) 所以需要+1
                if (!st[j])
                {
                    q[tt ++ ] = j;
                    st[j] = true;
                }
            }
        }
    }

    return true;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    while (m -- )
    {
        int x, a, b;
        scanf("%d%d%d", &x, &a, &b);
        if (x == 1) add(b, a, 0), add(a, b, 0);
        else if (x == 2) add(a, b, 1);
        else if (x == 3) add(b, a, 0);
        else if (x == 4) add(b, a, 1);
        else add(a, b, 0);
    }

    for (int i = 1; i <= n; i ++ ) add(0, i, 1);//每个点都有连接一个1 表示至少一个糖果

    if (!spfa()) puts("-1");
    else
    {
        LL res = 0;
        for (int i = 1; i <= n; i ++ ) res += dist[i];
        printf("%lld\n", res);
    }

    return 0;
}


区间https://www.acwing.com/problem/content/364/

前缀和:
求满足区间的数字多少个 就是求最小->满足所有上界-> >=号
->由x0+c <= x1 是从x0建一条到c的边的 所以后面的也这么做
si: 集合从[1,n]中选择整数的个数.
1≤i≤50001. 下面用V=50001.

s0=0. 符合定义, 且作为求最小值的明确下界.

问题转换为求满足条件的sV最小值. 求最小值用最长路算法求解, 从原题中建立≥≥的关系.

不等式关系的建立:

si≥si−1,1≤i≤V: 前缀和含义的限制.

si−si−1≤1,1≤i≤V
si−1≥si−1: 保证整数i最多选一次.

区间[a,b]至少选c个: sb−sa−1≥c --> sb≥sa−1+c

转换为最长路问题:

将si作为顶点, 每个不等式关系作为有向边建图.

源点: 需要满足从源点出发能到达所有边. 考虑不等式关系si≥si−1,1≤i≤V 相当于有向边:

作者:代码改变头发
链接:https://www.acwing.com/solution/content/80472/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#include <cstring>
#include <iostream>

using namespace std;

const int V = 50001, N = V + 10, M = 50000 * 3 + 10;

int n;
int h[N], e[M], w[M], ne[M], idx;
int dist[N], q[N]; bool st[N]; 

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

void spfa()
{
    memset(dist, -0x3f, sizeof dist);
    dist[0] = 0;
    int hh = 0, tt = 0;
    q[tt ++] = 0; st[0] = true;

    while( hh != tt )
    {
        int u = q[hh ++];
        if( hh == N )   hh = 0;

        st[u] = false;

        for( int i = h[u]; ~i; i = ne[i] )
        {
            int v = e[i];
            if( dist[v] < dist[u] + w[i] )
            {
                dist[v] = dist[u] + w[i];
                if( !st[v] )
                {
                    st[v] = true;
                    q[tt ++] = v;
                    if( tt == N )   tt = 0;
                }
            }
        }
    }
}

int main()
{
    cin >> n;

    memset(h, -1, sizeof h);
    for( int i = 1; i <= V; i ++ )
    {
// 0<=s[i]-s[i-1]<=1 这里是前缀和要求
        add(i - 1, i, 0); //s(i) >= s(i-1) + 0 
        add(i, i - 1, -1); //s(i - 1) >= s(i) - 1表示最多多选一个数 
    }

    for( int i = 0; i < n; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        a ++, b ++; // + bias
        add(a - 1, b, c); //s(b) >= s(a-1) + c
    }

    spfa();
    cout << dist[V] << endl; //s(50001) 

    return 0;
}


posted @ 2022-05-16 19:21  liang302  阅读(31)  评论(0编辑  收藏  举报