其他分享
首页 > 其他分享> > 差分约束 利用图论的不等式求变量

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

作者:互联网

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

=表示+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的最短路

求最小值 ->最长路 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;
}


求最长路

前缀和:

si: 集合从[1,n]中选择整数的个数.
1≤i≤50001. 下面用V=50001V=50001.

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

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

不等式关系的建立:

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 ++ )
    {
        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;
}


标签:图论,dist,idx,不等式,int,tt,差分,st,add
来源: https://www.cnblogs.com/liang302/p/16278211.html