BZOJ2330 差分约束之浅谈数学不等式拓扑逻辑顺序转化为图论中队列实现广度优先搜索的最长路之双端队列Bellman-Ford算法及入队次数判断自环

大家都很强, 可与之共勉 。

这个标题是参考神犇LWD取的

题目链接

题意:
n个人分糖,保证每个人都有糖,有k个限制条件,分别是a=b,a < b,a≥b,a > b,a≤b。这五种情况分别用x=1,2,3,4,5表示。求最少需要准备多少糖果。其中n,k≤10^6。

差分约束

顺便说一下差分约束,网上都讲得不详细。

一群知其然而不知其所以然的辣鸡

差分约束系统就是说的是满足一些带等号的不等式的一个系统。我们可以通过建立图论模型的方式求解。问的一般情况是最小值或者最大值是什么。

记住一个结论:
差分约束中求最小值用≥,跑最长路;求最大值用≤,跑最短路。

为什么这么做?

dis[u]表示的是u – s ( s为起点 ) 的最值。

那么如果是求最小值,所有的不等式都应该形如C < A – B, C为常数。然后B向A建立一条权值为C的边,dis [A] – dis [B] 最后就表示题目中的最值。henrongyixiang

题解
那么题中求的是最小值,就用 <= 然后化成C < A – B…

x=1即a=b,直接a→b,b→a权值都是0;
x=3即a≥b,直接b→a,权值为0;
x=5即a≤b,直接a→b,权值为0;
那么不带等号的怎么办呢?
(如果是实数可以不管,就是求得的最值取不到也在误差范围内。)
因为a,b均是整数,所以
x=2即a < b⇒a≤b−1,然后a→b,权值为1;
同理,x=4即a>b⇒a≥b+1,然后b→a,权值为1;
然后是与源点S连边。因为每个人都有糖,即dis[i]≥1⇒dis[i]−dis[S]≥1,所以S→i,权值为1。(直接放到队列里面就好了)

# include <bits/stdc++.h>

inline int readInt ( )  {
    static char buf [1 << 18 | 1], *ss, *tt ;
    # define pick( ) ( ( ss == tt ) ? ( tt = buf + fread ( ss = buf, 1, 1 << 18 | 1, stdin ), ( ss == tt ) ? -1 : *ss ++ ) : *ss ++ )
    register int x, c ;
    while ( ! isdigit ( c = pick ( ) ) ) ;
    for ( x = -48 + c ; isdigit ( c = pick ( ) ) ; ( x *= 10 ) += c - 48 ) ;
    # undef pick
    return x ;
}

const int N = 1234567 ;

struct edge  {
    int to, w ;
    edge* nxt ;
} g [N << 1], *NewEdge = g, *head [N] ;

inline int add_edge ( int u, int v, int w )  {
    *( ++ NewEdge ) = ( edge ) {  v, w, head [u]  } ; head [u] = NewEdge ;
}

inline bool chkmax ( int& d, const int& x )  {
    return ( d < x ) ? d = x, 1 : 0 ;
}

int dis [N] ;
int cnt [N] ;
std :: bitset < N > inq ;
std :: deque < int > Q ;

int main ( )  {
    int n ( readInt ( ) ), k ( readInt ( ) ) ;
    while ( k -- )  {
        int x ( readInt ( ) ), a ( readInt ( ) ), b ( readInt ( ) ) ;
        switch ( x )  {
            case 1 :  {
                add_edge ( a, b, 0 ) ;
                add_edge ( b, a, 0 ) ;
                break ; 
            }
            case 2 :  {
                if ( a == b )   return puts ( "-1" ), 0 ;
                add_edge ( a, b, 1 ) ;
                break ; 
            }
            case 3 :  {
                add_edge ( b, a, 0 ) ;
                break ; 
            }
            case 4 :  {
                if ( a == b )   return puts ( "-1" ), 0 ;
                add_edge ( b, a, 1 ) ;
                break ; 
            }
            case 5 :  {
                add_edge ( a, b, 0 ) ;
                break ; 
            }
        }
    }

    inq.reset ( ) ;
    Q.push_front ( 1 ) ;  dis [1] = 1 ; inq [1] = 1 ;
    for ( register int i = 2 ; i <= n ; ++ i )  {
        Q.push_back ( i ) ; // !!!
        dis [i] = 1 ;
        inq [i] = 1 ;
    }
    while ( ! Q.empty ( ) )  {
        int u = Q.front ( ) ; Q.pop_front ( ) ;
        inq [u] = 0 ;
        for ( edge* it = head [u] ; it ; it = it -> nxt )  {
            int v = it -> to ;
            if ( chkmax ( dis [v], dis [u] + it -> w ) )  {
                if ( ! inq [v] )  {
                    if ( ++ cnt [v] > n )  {
                        return puts ( "-1" ), 0 ;
                    }
                    ( Q.empty ( ) || dis [Q.front ( )] < dis [v] ) ? Q.push_front ( v ) : Q.push_back ( v ) ;
                    inq [v] = 1 ;
                }
            }
        }
    }
    long long ans ( 0 ) ;
    for ( register int i = 1 ; i <= n ; ++ i )  ans += dis [i] ;
    return std :: cout << ans << std :: endl, 0 ;
}
    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/simpsonk/article/details/78166148
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞