大家都很强, 可与之共勉 。
我们拿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" ) ;
}
}
所以后两种写法是优秀的。