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

6 篇文章 0 订阅

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

我们拿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" ) ;
    }
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值