大家都很强, 可与之共勉。
题意:
给定有向图
G=(V,E)
。设
P
是
题解:
转化为二分图坠大匹配的问题,把每个点
u
拆为左边的点
求出它的坠大匹配,用总点数减去就得到了最小路径覆盖。
证明:
我们认为一开始每个点都是独立的为一条路径,总共有 n 条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了
1 。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。
输出方案的话,直接在里面找流量为
0
<script type="math/tex" id="MathJax-Element-20">0</script>的边就好了……详见代码
# include <bits/stdc++.h>
# define N 2010
class Network {
private :
struct edge {
int to, w, nxt ;
edge ( ) { }
edge ( int to, int w, int nxt ) : to ( to ), w ( w ), nxt ( nxt ) { }
} g [60010 << 1] ;
int head [N], cur [N], ecnt ;
int S, T , dep [N] ;
inline int dfs ( int u, int a ) {
if ( u == T || ! a ) return a ;
int flow = 0, v, f ;
for ( int& i = cur [u] ; i ; i = g [i].nxt ) {
v = g [i].to ;
if ( dep [v] == dep [u] + 1 ) {
f = dfs ( v, std :: min ( g [i].w, a - flow ) ) ;
g [i].w -= f, g [i ^ 1].w += f ;
flow += f ;
if ( a == flow ) return a ;
}
}
if ( ! flow ) dep [u] = -1 ;
return flow ;
}
inline bool bfs ( int S, int T ) {
static std :: queue < int > q ;
memset ( dep, 0, sizeof ( int ) * ( T + 1 ) ) ;
dep [S] = 1 ;
q.push ( S ) ;
while ( ! q.empty ( ) ) {
int u = q.front ( ) ; q.pop ( ) ;
for ( int i = head [u] ; i ; i = g [i].nxt ) {
int& v = g [i].to ;
if ( g [i].w && ! dep [v] ) {
dep [v] = dep [u] + 1 ;
q.push ( v ) ;
}
}
}
return dep [T] ;
}
public :
Network ( ) { ecnt = 1 ; }
inline void add_edge ( int u, int v, int w ) {
g [++ ecnt] = edge ( v, w, head [u] ) ; head [u] = ecnt ;
g [++ ecnt] = edge ( u, 0, head [v] ) ; head [v] = ecnt ;
}
inline int dinic ( int S, int T ) {
this -> S = S, this -> T = T ;
int rt = 0 ;
while ( bfs ( S, T ) ) {
memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ;
rt += dfs ( S, 0x3f3f3f3f ) ;
}
return rt ;
}
void display ( int n, int S, int T ) {
int ans = n - dinic ( S, T ) ;
static std :: bitset < N > vis ;
vis.reset ( ) ;
for ( int i = 1 ; i <= n ; ++ i )
if ( ! vis.test ( i ) ) {
int curr = i ;
for ( bool found = 0 ; ; found = 0 ) {
printf ( "%d ", curr ) ;
vis.set ( curr ) ;
for ( int i = head [curr] ; i ; i = g [i].nxt ) {
int& v = g [i].to ;
if ( g [i].w == 0 && v > n && v <= 2 * n ) {
curr = v - n ;
found = 1 ;
break ;
}
}
if ( found == 0 ) {
puts ( "" ) ;
break ;
}
}
}
printf ( "%d\n", ans ) ;
}
} Lazer ;
int main ( ) {
int n, m ;
scanf ( "%d%d", & n, & m ) ;
const int S = n * 2 + 1, T = n * 2 + 2 ;
for ( int i = 1 ; i <= n ; ++ i ) {
Lazer.add_edge ( S, i, 1 ) ;
Lazer.add_edge ( i + n, T, 1 ) ;
}
while ( m -- ) {
static int u, v ;
scanf ( "%d%d", & u, & v ) ;
Lazer.add_edge ( u, v + n, 1 ) ;
}
Lazer.display ( n, S, T ) ;
return 0 ;
}