BZOJ1715 SPFA 浅谈图论之负环的多重化判定

来源:互联网 发布:英语句子成分解析软件 编辑:程序博客网 时间:2024/05/18 02:06

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

我们拿BZOJ1715做一个例子。
题目很简单,就是要判是否存在负环。
注意加边的时候要正边权加双向边。

主要探讨一下几种写法的各自优秀之处

BFS版:

速度随机化起点后在72ms左右

判入队次数是否大于n次
如果是,则存在负环

/**************************************************************    Problem: 1715    User: Lazer2001    Language: C++    Result: Accepted    Time:72 ms    Memory:1376 kb****************************************************************/# include <bits/stdc++.h>inline bool chkmin ( int& d, const int& x )  {    return ( d > x ) ? d = x, 1 : 0 ;}struct edge  {    int to, w ;    edge* nxt ;} g [6456], *NewEdge, *head [567] ;inline int add_edge ( int u, int v, int w )  {    *( ++ NewEdge ) = ( edge ) {  v, w, head [u]  } ; head [u] = NewEdge ;}std :: queue < int > Q ;int cnt [567] ;int dis [567] ;bool inq [567] ;bool Spfa ( int S, int N )  {    memset ( cnt, 0, sizeof cnt ) ;    memset ( inq, 0, sizeof inq ) ;    memset ( dis, 0x3f, sizeof dis ) ;    while ( ! Q.empty ( ) ) Q.pop ( ) ;    Q.push ( S ) ;    dis [S] = 0 ;    inq [S] = 1 ;    cnt [S] = 1 ;    while ( ! Q.empty ( ) )  {        int u = Q.front ( ) ; Q.pop ( ) ;        inq [u] = 0 ;        for ( edge* it = head [u] ; it ; it = it -> nxt )  {            if ( chkmin ( dis [it -> to], dis [u] + it -> w ) )  {                if ( ! inq [it -> to] )  {                    Q.push ( it -> to ) ;                    if ( ++ cnt [it -> to] > N )  return true ;                }            }        }    }    return false ;}int main ( )  {    srand ( 20010608 ) ;    int T ;    scanf ( "%d", & T ) ;    while ( T -- )  {        NewEdge = g ;        memset ( head, 0, sizeof head ) ;        int n, m, o ;        scanf ( "%d%d%d", & n, & m, & o ) ;        while ( m -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, w ) ;            add_edge ( v, u, w ) ; // 这是什么设定!!! Why Double ???         }        while ( o -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, -w ) ;        }        int s = ( ( rand ( ) << 15 ) | rand ( ) ) % n + 1 ;        puts ( Spfa ( 1, n ) ? "YES" : "NO" ) ;    }}

DFS坑爹版

速度在430ms左右

/**************************************************************    Problem: 1715    User: Lazer2001    Language: C++    Result: Accepted    Time:432 ms    Memory:1372 kb****************************************************************/# include <bits/stdc++.h>inline bool chkmin ( int& d, const int& x )  {    return ( d > x ) ? d = x, 1 : 0 ;}struct edge  {    int to, w ;    edge* nxt ;} g [6456], *NewEdge, *head [567] ;inline int add_edge ( int u, int v, int w )  {    *( ++ NewEdge ) = ( edge ) {  v, w, head [u]  } ; head [u] = NewEdge ;}std :: queue < int > Q ;int dis [567] ;bool vis [567] ;inline bool spfa ( int u )  {    vis [u] = 1 ;    for ( edge* it = head [u] ; it ; it = it -> nxt )  {        if ( chkmin ( dis [it -> to], dis [u] + it -> w ) )  {            if ( vis [it -> to] )    return true ;            if ( spfa ( it -> to ) )  return true ;              }    }    return vis [u] = 0 ;}bool Spfa ( int n )  {    memset ( vis, 0, sizeof vis ) ;    memset ( dis, 0x3f, sizeof dis ) ;//  static int seq [567] ;//  for ( int i = 1 ; i <= n ; ++ i ) seq [i] = i ;//  std :: random_shuffle ( seq + 1, seq + 1 + n ) ;    for ( int i = 1 ; i <= n ; ++ i )  {        dis [i] = 0 ;        if ( spfa ( i ) )   return true ;    }    return false ;}int main ( )  {    int T ;    scanf ( "%d", & T ) ;    while ( T -- )  {        NewEdge = g ;        memset ( head, 0, sizeof head ) ;        int n, m, o ;        scanf ( "%d%d%d", & n, & m, & o ) ;        while ( m -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, w ) ;            add_edge ( v, u, w ) ; // 这是什么设定!!! Why Double ???         }        while ( o -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, -w ) ;        }        puts ( Spfa ( n ) ? "YES" : "NO" ) ;    }}

为什么慢???
因为这个写法太不优秀了,会重复搜索

改成这样:

inline bool spfa ( int u )  {    vis [u] = 1 ;    for ( edge* it = head [u] ; it ; it = it -> nxt )  {        if ( chkmin ( dis [it -> to], dis [u] + it -> w ) )  {            if ( vis [it -> to] )   return true ;            if ( spfa ( it -> to ) )  return true ;             }    }    return vis [u] = 0 ;}bool Spfa ( int n )  {    for ( int i = 1 ; i <= n ; ++ i )  {        memset ( vis, 0, sizeof vis ) ;        memset ( dis, 0, sizeof dis ) ;        if ( spfa ( i ) )   return true ;    }    return false ;}

因为是判负环,所以我们更本不需要处理dis数组,不需要求出具体的值,直接全为某一个数就好,这样更新次数少了很多,不需要把dis[起点]赋成0

/**************************************************************    Problem: 1715    User: Lazer2001    Language: C++    Result: Accepted    Time:32 ms    Memory:1372 kb****************************************************************/# include <bits/stdc++.h>inline bool chkmin ( int& d, const int& x )  {    return ( d > x ) ? d = x, 1 : 0 ;}struct edge  {    int to, w ;    edge* nxt ;} g [6456], *NewEdge, *head [567] ;inline int add_edge ( int u, int v, int w )  {    *( ++ NewEdge ) = ( edge ) {  v, w, head [u]  } ; head [u] = NewEdge ;}std :: queue < int > Q ;int dis [567] ;bool vis [567] ;inline bool spfa ( int u )  {    vis [u] = 1 ;    for ( edge* it = head [u] ; it ; it = it -> nxt )  {        if ( chkmin ( dis [it -> to], dis [u] + it -> w ) )  {            if ( vis [it -> to] )    return true ;            if ( spfa ( it -> to ) )  return true ;              }    }    return vis [u] = 0 ;}bool Spfa ( int n )  {    for ( int i = 1 ; i <= n ; ++ i )  {        memset ( vis, 0, sizeof vis ) ;        memset ( dis, 0, sizeof dis ) ;        if ( spfa ( i ) )   return true ;    }    return false ;}int main ( )  {    int T ;    scanf ( "%d", & T ) ;    while ( T -- )  {        NewEdge = g ;        memset ( head, 0, sizeof head ) ;        int n, m, o ;        scanf ( "%d%d%d", & n, & m, & o ) ;        while ( m -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, w ) ;            add_edge ( v, u, w ) ; // 这是什么设定!!! Why Double ???         }        while ( o -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, -w ) ;        }        puts ( Spfa ( n ) ? "YES" : "NO" ) ;    }}

代码2:

/**************************************************************    Problem: 1715    User: Lazer2001    Language: C++    Result: Accepted    Time:32 ms    Memory:1372 kb****************************************************************/# include <bits/stdc++.h>inline bool chkmin ( int& d, const int& x )  {    return ( d > x ) ? d = x, 1 : 0 ;}struct edge  {    int to, w ;    edge* nxt ;} g [6456], *NewEdge, *head [567] ;inline int add_edge ( int u, int v, int w )  {    *( ++ NewEdge ) = ( edge ) {  v, w, head [u]  } ; head [u] = NewEdge ;}std :: queue < int > Q ;int dis [567] ;bool vis [567] ;inline bool spfa ( int u )  {    vis [u] = 1 ;    for ( edge* it = head [u] ; it ; it = it -> nxt )  {        if ( chkmin ( dis [it -> to], dis [u] + it -> w ) )  {            if ( vis [it -> to] )    return true ;            if ( spfa ( it -> to ) )  return true ;              }    }    return vis [u] = 0 ;}bool Spfa ( int n )  {    memset ( vis, 0, sizeof vis ) ;    memset ( dis, 0, sizeof dis ) ;    for ( int i = 1 ; i <= n ; ++ i )  {        if ( spfa ( i ) )   return true ;    }    return false ;}int main ( )  {    int T ;    scanf ( "%d", & T ) ;    while ( T -- )  {        NewEdge = g ;        memset ( head, 0, sizeof head ) ;        int n, m, o ;        scanf ( "%d%d%d", & n, & m, & o ) ;        while ( m -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, w ) ;            add_edge ( v, u, w ) ; // 这是什么设定!!! Why Double ???         }        while ( o -- )  {            int u, v, w ;            scanf ( "%d%d%d", & u, & v, & w ) ;            add_edge ( u, v, -w ) ;        }        puts ( Spfa ( n ) ? "YES" : "NO" ) ;    }}

所以后两种写法是优秀的。

原创粉丝点击