[NOI2008] volunteer recruitment (linear programming - dual problem - cost flow)

Posted by KevinCB on Sun, 06 Mar 2022 08:57:04 +0100

problem

luogu-P3980

solution

Volunteers work continuously [ s i , t i ] [s_i,t_i] [si, ti] days, we can extract the model of the longest k-repeatable interval set problem in the twenty-four questions of network flow.

Similarly, put 1 ∼ n 1\sim n 1 ∼ n days abstract into one 1 ∼ n + 1 1\sim n+1 1 ∼ n+1 point chain.

  • Source point s → 1 s\rightarrow 1 s → 1 with infinite capacity and zero cost, n + 1 → t n+1\rightarrow t n+1 → t sink point has infinite capacity and zero cost.
  • then i → i + 1 i\rightarrow i+1 i → i+1 Capacity ∞ − a i \infty-a_i ∞ − ai cost zero.
  • For section i i i kind of volunteer, s i → t i + 1 s_i\rightarrow t_i+1 si → ti + 1 capacity infinite cost c i c_i ci​.

Finally, the answer is to run the minimum cost and the maximum flow.

If you can't directly understand the correctness of such a diagram, you can consider flowing the traffic in the network diagram.

If from s s s along the side of zero cost t t Tflow, because the side flow on the chain is ∞ − a i \infty-a_i ∞ − ai, so previously ∞ \infty ∞ cannot flow out.

Then it is necessary to pass the side stream with volunteers (cost).

Suppose the flow reaches the point i i i. Then the rest cannot flow ( i , i + 1 ) (i,i+1) The flow of (i,i+1) we have to start from i i i even go out of the volunteer side stream, and stream one will cost c i c_i The price of ci.

Then at point x + 1 x+1 When x+1, these flows will converge again.

This is equivalent to recruiting from i i i start to x x x end of volunteers. (of course, there may be more than one) x x x (end point)

Anyway, in the end t t When t, the total flow will converge to s s s starts the flow ∞ \infty ∞.

You can understand that a team of people break into the secret room to escape. To carry out multi person branch line tasks at a certain level, the large army needs to send some people to complete them, and then the main line team continues to go down to the main line tasks. At a certain level, some people can return to the team after completing their branch line tasks. At the end of customs clearance, everyone must come out of the main task checkpoint.

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
#define maxm 50000
#define int long long
#define inf 0x3f3f3f3f
struct node { int to, nxt, flow, cost; }E[maxm];
int head[maxn], dis[maxn], lst[maxn], vis[maxn], a[maxn];
int cnt = -1, n, m, s, t;
queue < int > q;

void addedge( int u, int v, int w, int c ) {
    E[++ cnt] = { v, head[u], w, c }; head[u] = cnt;
    E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}

bool SPFA() {
    memset( lst, -1, sizeof( lst ) );
    memset( dis, 0x3f, sizeof( dis ) );
    q.push( dis[s] = 0 );
    while( ! q.empty() ) {
        int u = q.front(); q.pop(); vis[u] = 0;
        for( int i = head[u];~ i;i = E[i].nxt ) {
            int v = E[i].to;
            if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {
                dis[v] = dis[u] + E[i].cost; lst[v] = i;
                if( ! vis[v] ) vis[v] = 1, q.push( v );
            }
        }
    }
    return ~ lst[t];
}

int MCMF() {
    int ans = 0;
    while( SPFA() ) {
        int flow = inf;
        for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )
            flow = min( flow, E[i].flow );
        for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {
            E[i ^ 1].flow += flow;
            E[i].flow -= flow;
            ans += flow * E[i].cost;
        }
    }
    return ans;
}

signed main() {
    memset( head, -1, sizeof( head ) );
    scanf( "%lld %lld", &n, &m );
    s = 0, t = n + 2;
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
    for( int i = 1;i <= n;i ++ ) addedge( i, i + 1, inf - a[i], 0 );
    addedge( s, 1, inf, 0 );
    addedge( n + 1, t, inf, 0 );
    for( int i = 1, u, v, w;i <= m;i ++ ) {
        scanf( "%lld %lld %lld", &u, &v, &w );
        addedge( u, v + 1, inf, w );
    }
    printf( "%lld\n", MCMF() );
    return 0;
}

Solution (flow balance)

Hypothetical total 3 3 Day 3, day 2 i i i-day recruitment p i p_i ♪ people.

There are three types of volunteers:

  • From the first 1 1 Work from day 1 to day 2 3 3 3 days at c 1 c_1 c1, recruited b 1 b_1 b1 people.
  • From the first 2 2 2 days working to the second day 3 3 3 days at c 2 c_2 c2, recruited b 2 b_2 b2 # people.
  • From the first 1 1 Work from day 1 to day 2 2 2 2 days at c 3 c_3 c3, recruited b 3 b_3 b3 # people.

Then there are the following inequalities:
{ b 1 + b 3 ≥ a 1 b 1 + b 2 + b 3 ≥ a 2 b 1 + b 2 ≥ a 3 \begin{cases} b_1+b_3\ge a_1\\b_1+b_2+b_3\ge a_2\\b_1+b_2\ge a_3 \end{cases} ⎩⎪⎨⎪⎧​b1​+b3​≥a1​b1​+b2​+b3​≥a2​b1​+b2​≥a3​​
Ji di i i The number of volunteers recruited in day i exceeds the minimum requirement d i d_i ♪ people, obviously d i ≥ 0 d_i\ge 0 di​≥0. Then it can be rewritten into the following equation:
{ p 1 = b 1 + b 3 = a 1 + d 1 p 2 = b 1 + b 2 + b 3 = a 2 + d 2 p 3 = b 1 + b 2 = a 3 + d 3 \begin{cases}p_1=b_1+b_3=a_1+d_1\\p_2=b_1+b_2+b_3=a_2+d_2\\p_3=b_1+b_2=a_3+d_3\end{cases} ⎩⎪⎨⎪⎧​p1​=b1​+b3​=a1​+d1​p2​=b1​+b2​+b3​=a2​+d2​p3​=b1​+b2​=a3​+d3​​
After making a difference between two adjacent equations, the items are sorted out as follows:
{ p 1 = b 1 + b 3 = a 1 + d 1 p 2 − p 1 = b 2 − b 3 = a 2 − a 1 + d 2 − d 1 p 3 − p 2 = − b 3 = a 3 − a 2 + d 3 − d 2 − p 3 = − b 1 − b 2 = − a 3 − d 3 ⇒ { p 1 − p 0 = b 1 + b 3 − a 1 − d 1 = 0 p 2 − p 1 = b 2 − b 3 − a 2 + a 1 − d 2 + d 1 = 0 p 3 − p 2 = − b 3 − a 3 + a 2 − d 3 + d 2 = 0 p 4 − p 3 = − b 1 − b 2 + a 3 + d 3 = 0 \begin{cases}p_1=b_1+b_3=a_1+d_1\\p_2-p_1=b_2-b_3=a_2-a_1+d_2-d_1\\ p_3-p_2=-b_3=a_3-a_2+d_3-d_2\\-p_3=-b_1-b_2=-a_3-d_3\end{cases}\Rightarrow \begin{cases}p_1-p_0=b_1+b_3-a_1-d_1=0\\p_2-p_1=b_2-b_3-a_2+a_1-d_2+d_1=0\\ p_3-p_2=-b_3-a_3+a_2-d_3+d_2=0\\p_4-p_3=-b_1-b_2+a_3+d_3=0 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧​p1​=b1​+b3​=a1​+d1​p2​−p1​=b2​−b3​=a2​−a1​+d2​−d1​p3​−p2​=−b3​=a3​−a2​+d3​−d2​−p3​=−b1​−b2​=−a3​−d3​​⇒⎩⎪⎪⎪⎨⎪⎪⎪⎧​p1​−p0​=b1​+b3​−a1​−d1​=0p2​−p1​=b2​−b3​−a2​+a1​−d2​+d1​=0p3​−p2​=−b3​−a3​+a2​−d3​+d2​=0p4​−p3​=−b1​−b2​+a3​+d3​=0​
In the network flow, except for the source and sink points, the other points should meet the flow balance, that is, the inflow flow is equal to the outflow flow; If the inflow is recorded as positive and the outflow is recorded as negative, the algebraic sum of inflow and outflow shall be satisfied 0 0 0.

A connection in the network diagram x , y x,y x. The side of Y is at x , y x,y x. Y appears once in the flow balance equation, and once is positive and once is negative.

Therefore, we can establish a point for each of the above equations. This equation represents the flow balance at this point.

Then look at the final equation:

observationⅠ. \text{observationⅠ.} observationⅠ. b i , d i b_i,d_i bi, di , both appear in exactly two equations and are positive and negative. So every variable b i , d i b_i,d_i bi # can be used as an edge in a graph.

observationⅡ. \text{observationⅡ.} observationⅡ. constant a i a_i ai , also happens to appear in the two equations and is positive and negative. When it is positive, it indicates inflow and can be connected with the source point; When it is negative, it indicates outflow and can be connected with the sink.

According to the difference rules, a i a_i ai must have appeared in i , i + 1 i,i+1 i. In the two equations I + 1, and must be the first i i The first equation is negative, and the second equation is negative i + 1 i+1 i+1 is positive.

The constant is connected with the source and sink points, and the variable represents the edge running balance between the constant points.

The final answer is min ⁡ ∑ b i ⋅ c i \min \sum b_i·c_i min Σ bi ⋅ ci can be expressed in the form of "cost".

  • Brief description of drawing construction method:

    hypothesis a 0 = a n + 1 = 0 a_0=a_{n+1}=0 a0​=an+1​=0.

    • Establish source and sink points s , t s,t s,t.
    • Establish point 1 ∼ n + 1 1\sim n+1 1 ∼ n+1, representing n + 1 n+1 n+1 equations.
    • The first i + 1 i+1 i+1 point to the second i i i points are connected to an edge with infinite capacity and zero cost. corresponding b i , d i b_i,d_i The balance of bi, di.
    • The first i i Class i volunteer connection s i → t i + 1 s_i\rightarrow t_i+1 si → ti + 1, infinite capacity, cost c i c_i ci​.
    • For section i i i points, if a i − a i − 1 a_i-a_{i-1} ai − ai − 1 − is positive, with edges s → i s\rightarrow i s → i, capacity a i − a i − 1 a_i-a_{i-1} ai − ai − 1 cost is zero; If it is negative, connect the edges i → t i\rightarrow t i → t, capacity a i − 1 − a i a_{i-1}-a_i ai − 1 − ai cost is zero. Equivalent to the constant term in the equation.

In practical understanding, the constant term can be mentioned to the right:
{ b 1 + b 3 − d 1 = a 1 − a 0 b 2 − b 3 − d 2 + d 1 = a 2 − a 1 − b 3 − d 3 + d 2 = a 3 − a 2 − b 1 − b 2 + d 3 = a 4 − a 3 \begin{cases} b_1+b_3-d_1=a_1-a_0\\ b_2-b_3-d_2+d_1=a_2-a_1\\ -b_3-d_3+d_2=a_3-a_2\\ -b_1-b_2+d_3=a_4-a_3 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧​b1​+b3​−d1​=a1​−a0​b2​−b3​−d2​+d1​=a2​−a1​−b3​−d3​+d2​=a3​−a2​−b1​−b2​+d3​=a4​−a3​​
hold a i − a i − 1 a_i-a_{i-1} ai − ai − 1 i i The gain and loss of i equation, so you can understand the meaning of the connection between positive and negative and source and sink.

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
#define maxm 50000
#define int long long
#define inf 0x3f3f3f3f
struct node { int to, nxt, flow, cost; }E[maxm];
int head[maxn], dis[maxn], lst[maxn], vis[maxn], a[maxn];
int cnt = -1, n, m, s, t;
queue < int > q;

void addedge( int u, int v, int w, int c ) {
    E[++ cnt] = { v, head[u], w, c }; head[u] = cnt;
    E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}

bool SPFA() {
    memset( lst, -1, sizeof( lst ) );
    memset( dis, 0x3f, sizeof( dis ) );
    q.push( dis[s] = 0 );
    while( ! q.empty() ) {
        int u = q.front(); q.pop(); vis[u] = 0;
        for( int i = head[u];~ i;i = E[i].nxt ) {
            int v = E[i].to;
            if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {
                dis[v] = dis[u] + E[i].cost; lst[v] = i;
                if( ! vis[v] ) vis[v] = 1, q.push( v );
            }
        }
    }
    return ~ lst[t];
}

int MCMF() {
    int ans = 0;
    while( SPFA() ) {
        int flow = inf;
        for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )
            flow = min( flow, E[i].flow );
        for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {
            E[i ^ 1].flow += flow;
            E[i].flow -= flow;
            ans += flow * E[i].cost;
        }
    }
    return ans;
}

signed main() {
    memset( head, -1, sizeof( head ) );
    scanf( "%lld %lld", &n, &m );
    s = 0, t = n + 2;
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
    for( int i = 1;i <= n;i ++ ) addedge( i + 1, i, inf, 0 );
    for( int i = 1, u, v, w;i <= m;i ++ ) {
        scanf( "%lld %lld %lld", &u, &v, &w );
        addedge( u, v + 1, inf, w );
    }
    for( int i = 1;i <= n + 1;i ++ )
        if( a[i] - a[i - 1] > 0 ) addedge( s, i, a[i] - a[i - 1], 0 );
        else addedge( i, t, a[i - 1] - a[i], 0 );
    printf( "%lld\n", MCMF() );
    return 0;
}

In fact, the understanding of the meaning of edge construction of flow balance is also from the dual perspective of linear programming. It will be explained in detail in the defensive front.

Topics: network-flows