Differential constraint summary

Differential constraint concept

If a system consists of n n n variables and m m m constraints, forming m m m shaped like x i − x j ≤ k x_i - x_j ≤ k The inequality of xi − xj ≤ k (i,j ∈ [1,n],k is a constant) is called a difference constrained system. That is, the difference constraint system is a method to solve a special set of inequalities about a set of variables.

Solving the differential constraint system can be transformed into a single source shortest path (or longest path) problem in graph theory.

observation x i − x j < = c k x_i - x_j <= c_k xi − xj < = CK, you will find that it is similar to the triangular inequality in the shortest path d i s [ v ] < = d i s [ u ] + w [ u , v ] dis[v] <= dis[u] + w[u,v] Dis [v] < = dis [u] + w [u, v], i.e d i s [ v ] − d i s [ u ] < = w [ u , v ] dis[v] - dis[u] <= w[u,v] dis[v]−dis[u]<=w[u,v]. Therefore, take each variable x i x_i xi is a node, for constraints x i − x j < = c k x_i - x_j <= c_k xi − xj < = CK, connecting an edge ( j , i ) (j, i) (j,i), the edge weight is c k c_k ck​. Let's add another source point s s s, s s s is connected to all fixed points, and the edge weight is 0 0 0 For this diagram, take s s s runs Bellman Ford algorithm (or SPFA algorithm) for the source point, and finally{ d i s [ i ] dis[i] dis[i]} is a set of feasible solutions.

**Explanation: * * the form of inequality is equivalent to the shortest path solution process in graph theory, so the inequality problem with difference constraints is transformed into graph theory problem

Find the maximum or minimum value of the variable (solving problem)

**Conditions to be satisfied by the source point: * * starting from the source point, you can go to all edges

**Conclusion: * * if the minimum value is obtained, the longest path should be obtained; If the maximum value is obtained, the shortest circuit shall be obtained;

**Question: * * how to convert x i < = C x_i <= C xi < = C, where c c c is a constant, this kind of inequality

**Method: * * create a virtual source point 0, and then create 0 − > i 0->i 0 − > I, length is c c c is enough.

in order to x i x_i Take the maximum value of xi , as an example: find all slave x i x_i xi , the inequality chain is formed x i < = x j + c 1 < = x k + c 2 + c 1 < = . . . . < = x 0 + c 1 + c 2 + . . . x_i <= x_j+ c_1 <= x_k + c_2 + c_1 <= .... <= x_0 + c_1 + c_2 + ... xi < = XJ + c1 < = XK + c2 + c1 < =... < = x0 + c1 + c2 +..., where x 0 x_0 x0 , is the virtual source point. If the initial value is known, it can be calculated x i x_i Upper bound of a range of xi +
final x i x_i The maximum value of xi is equal to the minimum value of all upper bounds. The reason is shown in the figure below (similar to short board effect)

To sum up, find the maximum value of variables (both < = <= < = inequality), that is, find the minimum of all upper bounds, that is, find the shortest path on the graph; Similarly, to find the maximum value of the variable is to find the longest path on the graph
When finding the shortest path, if there is a negative ring on the graph, the variable is misunderstood; When finding the longest path, if there is a positive ring on the graph, the variable has no solution

Finding the feasible solution of inequality system (deterministic problem)

**Conditions to be satisfied by the source point: * * starting from the source point, you can go to all edges

Steps:

  1. First, each inequality x i < = x j + c k x_i <= x_j + c_k xi < = XJ + ck, converted to a from x j x_j xj , walk to x i x_i xi, length c k c_k One side of ck
  2. Find a super source point so that the source point can traverse all edges
  3. Find the single source shortest path from the source point

**Result 1: * * if there is a negative ring, the original inequality system must have no solution
**Result 2: * * if there is no negative ring, dis[i] is a feasible solution of the original inequality system

Note: why does the condition require that the source point must be able to reach all edges? Why not all points?
Each edge is a constraint, and the difference constraint is to meet all constraints, so it must be ensured that all edges can go to ensure that all constraints are met
If some points are isolated points, it doesn't matter if you can't get there. If you can't get there, it means that there are no restrictions on the point, and its value is arbitrary

SPFA solution

  1. Application example of finding the maximum or minimum value of a variable
    Title Description

Analytical method
Since the solution of this problem is the minimum value, the following equations can be obtained from the meaning of the problem

However, the error prone point of the difference constraint problem is that the unequal relationship is not comprehensive. In this problem, there is an easily ignored requirement that every child should be given candy. That is, if set s [ i ] s[i] s[i] for children i i i the number of sweets allocated should also be related s [ i ] > = 1 s[i] >= 1 s[i]>=1. In order to meet the requirements of differential constraint form, a point with a value of 0 can be set s [ 0 ] s[0] s[0], then the above unequal relation can be written as s [ i ] > = s [ 0 ] + 1 s[i] >= s[0] + 1 s[i]>=s[0]+1

Then, considering the conditions for the transformation of difference constraints into graph theory problems, we can go to all edges from the source point. obviously, s [ 0 ] s[0] s[0] can meet the requirements. So from s [ 0 ] s[0] s[0] starts SPFA (to judge whether there is a solution, i.e. whether there is a positive ring in the figure), and adding the number of sweets of all children is the final answer

code implementation

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

using namespace std;
using LL = long long;

const int N = 1e5 + 10, M = 3e5 + 10;

int n, m;
int h[N], e[M], ne[M], w[M], idx;
stack<int> q;
bool st[N];
int cnt[N], dis[N];

void add(int a, int b, int c)
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}
bool spfa()
{
    q.push(0);
    st[0] = true;
    
    while (q.size())
    {
        int t = q.top();
        q.pop();
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int p = e[i];
            if (dis[p] < dis[t] + w[i])
            {
                dis[p] = dis[t] + w[i];
                cnt[p] = cnt[t] + 1;
                
                if (cnt[p] >= n + 1) return true;
                if (!st[p])
                {
                    q.push(p);
                    st[p] = true;
                }
            }
        }
    }

    return false;
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while (m --)
    {
        int x, a, b;
        cin >> x >> a >> b;
        if (x == 1) add(a, b, 0), add(b, a, 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);
    
    if (spfa()) cout << -1 << endl;
    else 
    {
        LL sum = 0;
        for (int i = 1; i <= n; ++ i) sum += dis[i];
        cout << sum << endl;
    }

    return 0;
}
  1. Finding the feasible solution of inequality system
    Title Description

Analytical method
set up n u m [ i ] num[i] num[i] indicates that the starting working time of a given cashier is i i i number of persons n u m [ i ] num[i] num[i]
s [ i ] s[i] s[i] indicates R [ 0 ] R[0] R[0] to R [ i ] R[i] R[i] the number of cashiers allocated in the corresponding time period is s [ i ] s[i] s[i]
r [ i ] r[i] r[i] represents time i i i the number of cashiers required is r [ i ] r[i] r[i]
The above statement is more abstract. Take the example (in order to use prefix and, the data is migrated backward by one bit as a whole)

s [] is to be evaluated

0, 23, 22, 1, 10 are converted to 1, 24, 23, 2, 11 due to prefix and

num[1]num[2]num[3]num[4]num[5]num[6]num[7]num[8]num[9]num[10]num[11]num[12]num[13]num[14]num[15]num[16]num[17]num[18]num[19]num[20]num[21]num[22]num[23]num[24]
110000000010000000000011

1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

r[1]r[2]r[3]r[4]r[5]r[6]r[7]r[8]r[9]r[10]r[11]r[12]r[13]r[14]r[15]r[16]r[17]r[18]r[19]r[20]r[21]r[22]r[23]r[24]
101000100000000000000001

The following unequal relations can be obtained from the meaning of the question

  1. 0 ≤ s i − s i − 1 ≤ n u m [ i ]   ,   1 ≤ i ≤ 24 0 \leq s_i - s_{i - 1} \leq num[i] \ , \ 1 \leq i \leq 24 0≤si​−si−1​≤num[i] , 1≤i≤24

  2. i ≥ 8   ,   s i − s i − 8 ≥ r i i \geq 8 \ , \ s_i - s_{i - 8} \geq r_i i≥8 , si​−si−8​≥ri​

    0 < i < 7   ,   s i + s 24 − s i + 16 ≥ r i 0 < i < 7 \ , \ s_i + s_{24} - s_{i + 16} \geq r_i 0<i<7 , si​+s24​−si+16​≥ri​

Derived

  1. s i ≥ s i − 1 + 0 s_i \geq s_{i - 1} + 0 si​≥si−1​+0

  2. s i − 1 ≥ s i − n u m [ i ] s_{i - 1} \geq s_i - num[i] si−1​≥si​−num[i]

  3. i ≥ 8   ,   s i ≥ s i − 8 + r i i \geq 8 \ , \ s_i \geq s_{i - 8} + r_i i≥8 , si​≥si−8​+ri​

  4. 0 < i < 7   ,   s i ≥ s i + 16 − s 24 + r i 0 < i < 7 \ , \ s_i \geq s_{i + 16} - s_{24} + r_i 0<i<7 , si​≥si+16​−s24​+ri​

The first three items meet the formal requirements that the difference constraint contains only two variables, but there are three variables in Item 4, of which, s 24 s_{24} s24 is the value of the required solution
The correct method is to traverse from small to large s 24 s_{24} All possible values of s24 , and the value satisfying all inequality requirements for the first time is the answer
The solution process is to find the feasible solution of the inequality system

code implementation

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

using namespace std;

const int N = 30, M = 100;

int n, m;
int h[N], e[M], ne[M], w[M], idx;
bool st[N];
int cnt[N], dis[N];
int x[N], s[N], r[N], num[N];
queue<int> q;

void add(int a, int b, int c)
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}
void build(int x)
{
    idx = 0;
    memset(h, -1, sizeof h);
    add(0, 24, x), add(24, 0, -x); // Specify s[24] = x, which is also a limiting condition added, s[24] = x < = > s [24] > = x & & s [24] < = X
    // In fact, there is no need to judge whether r[i] is 0, because even if it is 0, it is just a qualification of > = 0
    // for (int i = 1; i <= 24; ++ i)
    //     if (r[i])
    //     {
    //         if (i >= 8) add(i - 8, i, r[i]);
    //         else add(i + 16, i, r[i] - x);
    //     }
    for (int i = 1; i < 8; ++ i) add(i + 16, i, r[i] - x);
    for (int i = 8; i <= 24; ++ i) add(i - 8, i, r[i]);
    for (int i = 0; i <= 23; ++ i)
    {
        add(i, i + 1, 0);
        add(i + 1, i, -num[i + 1]);
    }
}
bool spfa(int x)
{
    build(x); // Construct a graph with x as s[24]

    memset(st, 0, sizeof st);
    memset(dis, -0x3f, sizeof dis);
    memset(cnt, 0, sizeof cnt);
    
    dis[0] = 0;
    q.push(0);
    st[0] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int p = e[i];
            if (dis[p] < dis[t] + w[i])
            {
                dis[p] = dis[t] + w[i];
                cnt[p] = cnt[t] + 1;

                if (cnt[p] >= 24) return true;
                if (!st[p])
                {
                    q.push(p);
                    st[p] = true;
                }
            }
        }
    }

    return false;
}

int main()
{
    int T;
    cin >> T;
    while (T --)
    {
        for (int i = 1; i <= 24; ++ i) cin >> r[i];
        cin >> n;
        memset(num, 0, sizeof num);
        while (n --)
        {
            int t;
            cin >> t;
            ++ t;
            ++ num[t];
        }

        bool flag = false;
        // Judge the legitimacy of a given s[24]. From small to large, the first legal value is the minimum value required by the answer
        for (int i = 0; i <= 1000; ++ i) // Enumerations [24]
            if (!spfa(i))
            {
                cout << i << endl;
                flag = true;
                break;
            }
        
        if (!flag) cout << "No Solution" << endl;
    }

    return 0;
}

Tarjan strongly connected component reduced point method

The actual performance of SPFA in the face of different data is unstable. For the sake of insurance, Tarjan strong connected component reduction can be used, and the complexity is relatively stable

Title Description
In order to better compare SPFA solution and Tarjan strongly connected component solution, the same topic is used for explanation

Algorithm idea
From a macro perspective:
In a directed ring graph, there are loops because of the dependence and nonlinear arrangement, so it is necessary to use SPFA to judge the loop and solve the maximum value

However, for topological graphs, dependencies are unidirectional. If all points are traversed according to the topological order, the requirements of all points can be maintained with linear complexity, and the final sum can be obtained

In terms of details:

  1. The first step is to convert the directed ring graph into DAG, and use the process reference of Tarjan shrinking point Previous writing that will do
  2. Establish a reduced point diagram, and verify whether there is a feasible solution at the same time
    The general solution is to count each scc edge weight sum. If the edge weight sum is regular and represents the existence of a positive ring, it is no solution in this problem; However, in this problem, we can ensure that the edge weight is non negative, that is, only one positive weight edge means that there is a positive ring, that is, there is no solution to the problem
  3. Traverse the whole graph according to the topological order (scc number from large to small) and maintain the required value of each point (Tarjan algorithm can ensure that the larger the obtained scc number value, the higher the corresponding priority)
    At this time, the traversal is carried out on the graph after shrinking the point, and a scc will be equivalent to a point. One doubt caused by this is whether the update results of so many points in a scc are the same for the points in other scc? Why can one point be equivalent to all points of a scc
    The reason is that the inner edge weight of any scc can be guaranteed to be 0 (if there is a non-0 edge weight, there is no solution) s c c a scc_a Points in scca + p 1 p_1 p1​, p 2 p_2 p2​, s c c b scc_b Points in sccb q 1 q_1 q1, use p 1 p_1 p1 update q 1 q_1 q1) and p 2 p_2 p2 update q 1 q_1 q1 , the results are the same, so you can use s c c a scc_a scca is equivalent to all points in it
  4. After traversing the shrinking point, all the objects in the graph s c c scc scc, cumulative summation is enough (note that summation requires summation of all points, which needs to be used s c c scc Value of scc* s c c scc Number of points in scc)

code implementation

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>

using namespace std;
using LL = long long;

const int N = 1e5 + 10, M = 6 * N; // The maximum number of edges given in the topic = 2*N, and the number of edges starting from 0 = N. It is necessary to build a graph twice, a total of 6*N

int n, m;
int h[N], hs[N], e[M], ne[M], w[M], idx;
stack<int> stk;
bool in_stk[N];
int id[N], Size[N], scc_cnt;
int dfn[N], low[N], timestamp;
int dis[N];

void add(int *h, int a, int b, int c)
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp;
    stk.push(u); in_stk[u] = true;

    for (int i = h[u]; ~i; i = ne[i])
    {
        int p = e[i];
        if (!dfn[p])
        {
            tarjan(p);
            low[u] = min(low[u], low[p]);
        }
        else if (in_stk[p]) low[u] = min(low[u], dfn[p]);
    }

    if (dfn[u] == low[u])
    {
        int y;
        ++ scc_cnt;
        do {
            y = stk.top(); stk.pop();
            id[y] = scc_cnt;
            in_stk[y] = false;
            ++ Size[scc_cnt];
        } while (y != u);
    }

}
int main()
{
    memset(h, -1, sizeof h);
    memset(hs, -1, sizeof hs);
    
    cin >> n >> m;
    // First drawing
    for (int i = 0; i < m; ++ i)
    {
        int t, a, b;
        cin >> t >> a >> b;

        if (t == 1) add(h, a, b, 0), add(h, b, a, 0);
        else if (t == 2) add(h, a, b, 1);
        else if (t == 3) add(h, b, a, 0);
        else if (t == 4) add(h, b, a, 1);
        else add(h, a, b, 0);
    }
    for (int i = 1; i <= n; ++ i) add(h, 0, i, 1);

    // for (int i = 0; i <= n; ++ i)
    //     if (!dfn(i)) tarjan(i);
    // In this question, point 0 is the super source point. You can go to all points from this point, so you can start tarjan from point 0
    tarjan(0);
    
    bool flag = true;
    for (int i = 0; i <= n; ++ i)
    {
        for (int j = h[i]; ~j; j = ne[j])
        {
            int p = e[j];
            int a = id[i], b = id[p];
            // Verify whether there is a feasible solution to verify whether there is a positive weight edge in the same scc
            if (a == b)
            {
                if (w[j] > 0)
                {
                    flag = false;
                    break;
                }
            }
            else
                add(hs, a, b, w[j]); // The number of schemes is not required, so multiple edges can be established without judgment
        }
        if (!flag) break;
    }

    if (!flag) cout << -1 << endl;
    else 
    {
        // Recursively solve the required minimum value of each point
        for (int i = scc_cnt; i >= 1; -- i)
            for (int j = hs[i]; ~j; j = ne[j])
            {
                int p = e[j];
                dis[p] = max(dis[p], dis[i] + w[j]);
            }
        
        LL sum = 0;
        for (int i = 1; i <= scc_cnt; ++ i) sum += (LL)dis[i] * Size[i]; // The points here are scc. All points to be calculated are counted, so * Size[i] is required

        cout << sum << endl;
    }
    return 0;
}

Tags: Algorithm

Posted on Wed, 06 Oct 2021 20:06:23 -0400 by RadGH