大家都很強, 可與之共勉 。
題意:
給定一個由
分別遵守以下規則:
- 從梯形的頂至底的
m 條路徑互不相交; - 從梯形的頂至底的
m 條路徑僅在數字結點處相交; 從梯形的頂至底的
m 條路徑允許在數字結點相交或邊相交。要求輸出三個任務的的最大值。
題解:
墜大費用墜大流(這名字好迷)
第一個任務 TASK1:
老生常談的每個點只經過一次
建圖:
建立超級源點
S ,和匯點T ,S 向第一層的點分別連一條(S→a1) 的弧(其實(S→a2) 也是茲磁的),容量爲1,費用爲0(如果是a2就是權值v) 。最後一層的點向匯點T 連邊,a2 連向T 容量爲爲1 ,費用爲0 。
每一個點a ,a1→a2 連邊,權值爲點權值v 。對於a 走到b ,a2→b1 連一條容量爲爲1 ,費用爲0 的邊。
如此一來一定保證了每個點只被經過一次,一定有m 條不相交不重合的路徑。
對於最大權值,直接跑最大費用最大流就可以了。
第二個任務 TASK2:
每個點不要求只經過一次了,但是每條邊只能經過一次。
我們考慮如何在上面的方法改進。
方法一:不拆點,限制邊的流量爲
方法二:拆點,把
啦啦啦啦
第三個任務 TASK3:
亂跑就好了
我們考慮如何在上面的方法改進。
方法一:不拆點,限制邊的流量爲
方法二:拆點,把
然後我調了一陣子發現時建圖邊界GG了,注意S的dis初值要足夠大
# include <bits/stdc++.h>
template < class T > inline bool chkmax ( T& d, const T& x ) { return d < x ? ( d = x ), 1 : 0 ; }
template < class T > inline bool chkmin ( T& d, const T& x ) { return d > x ? ( d = x ), 1 : 0 ; }
# define oo 0x3f3f3f3f
# define N 5010
# define M 16010
class MaxCostMaxFlow {
private :
struct edge {
int to, nxt, w, cost ;
} g [M << 1] ;
int S, T ;
int head [N], dis [N], pre [N], ecnt ;
inline bool spfa ( int S, int T ) {
static std :: bitset < N > inq ;
static std :: deque < int > Q ;
inq.reset ( ) ; Q.clear ( ) ;
memset ( pre, 0, sizeof ( int ) * ( T + 1 ) ) ;
memset ( dis, -1, sizeof ( int ) * ( T + 1 ) ) ;
Q.push_front ( S ) ;
inq [S] = 1 ;
dis [S] = 0x3f3f3f3f ; // big enough !!!
while ( ! Q.empty ( ) ) {
int u = Q.front ( ) ; Q.pop_front ( ) ;
inq [u] = 0 ;
for ( int i = head [u] ; i ; i = g [i].nxt ) {
int& v = g [i].to ;
if ( g [i].w && chkmax ( dis [v], dis [u] + g [i].cost ) ) {
pre [v] = i ;
if ( ! inq [v] ) {
( Q.empty ( ) || dis [v] > dis [Q.front ( )] ) ? Q.push_front ( v ) : Q.push_back ( v ) ;
inq [v] = 1 ;
}
}
}
}
return ( bool ) pre [T] ;
}
public :
MaxCostMaxFlow ( ) { ecnt = 1 ; memset ( head, 0, sizeof head ) ; }
inline void clear ( ) {
ecnt = 1 ; memset ( head, 0, sizeof head ) ;
}
inline void add_edge ( int u, int v, int w, int cost ) {
g [++ ecnt] = ( edge ) { v, head [u], w, cost } ; head [u] = ecnt ;
g [++ ecnt] = ( edge ) { u, head [v], 0, -cost } ; head [v] = ecnt ;
}
std :: pair < int, int > mcmf ( int S, int T ) {
this -> S = S, this -> T = T ;
int flow = 0, cost = 0, x ;
while ( spfa ( S, T ) ) {
x = oo ;
for ( int i = pre [T] ; i ; i = pre [g [i ^ 1].to] ) chkmin ( x, g [i].w ) ;
for ( int i = pre [T] ; i ; i = pre [g [i ^ 1].to] ) {
g [i].w -= x, g [i ^ 1].w += x ;
cost += x * g [i].cost ;
}
flow += x ;
}
return std :: make_pair ( flow, cost ) ;
}
} Lazer ;
int n, m ;
int a [50] [50] ;
int id1 [50] [50], id2 [50] [50] ;
void Solve1 ( int S, int T ) {
Lazer.clear ( ) ;
for ( int i = 1 ; i <= m ; ++ i ) Lazer.add_edge ( S, id1 [1] [i], 1, 0 ) ;
for ( int i = 1 ; i <= m + n - 1 ; ++ i ) Lazer.add_edge ( id2 [n] [i], T, 1, 0 ) ;
for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j <= i + m - 1 ; ++ j ) {
Lazer.add_edge ( id1 [i] [j], id2 [i] [j], 1, a [i] [j] ) ;
int x = i + 1, y = j + 1 ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id2 [i] [j], id1 [x] [y], 1, 0 ) ;
x = i + 1, y = j ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id2 [i] [j], id1 [x] [y], 1, 0 ) ;
}
printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}
void Solve2 ( int S, int T ) {
Lazer.clear ( ) ;
for ( int i = 1 ; i <= m ; ++ i ) Lazer.add_edge ( S, id1 [1] [i], 1, a [1] [i] ) ;
for ( int i = 1 ; i <= m + n - 1 ; ++ i ) Lazer.add_edge ( id1 [n] [i], T, oo, 0 ) ;
for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j <= i + m - 1 ; ++ j ) {
int x = i + 1, y = j + 1 ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id1 [i] [j], id1 [x] [y], 1, a [x] [y] ) ;
x = i + 1, y = j ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id1 [i] [j], id1 [x] [y], 1, a [x] [y] ) ;
}
printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}
void Solve3 ( int S, int T ) {
Lazer.clear ( ) ;
for ( int i = 1 ; i <= m ; ++ i ) Lazer.add_edge ( S, id1 [1] [i], 1, a [1] [i] ) ;
for ( int i = 1 ; i <= m + n - 1 ; ++ i ) Lazer.add_edge ( id1 [n] [i], T, oo, 0 ) ;
for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j <= i + m - 1 ; ++ j ) {
int x = i + 1, y = j + 1 ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id1 [i] [j], id1 [x] [y], oo, a [x] [y] ) ;
x = i + 1, y = j ;
if ( x <= n && y <= i + m - 1 + 1 ) Lazer.add_edge ( id1 [i] [j], id1 [x] [y], oo, a [x] [y] ) ;
}
printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}
int main ( ) {
// freopen ( "in.txt", "r", stdin ) ;
int cnt ( 0 ) ;
scanf ( "%d%d", & m, & n ) ;
for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j <= i + m - 1 ; ++ j ) {
scanf ( "%d", a [i] + j ) ;
id1 [i] [j] = ++ cnt ;
id2 [i] [j] = ++ cnt ;
}
const int S = cnt + 1, T = cnt + 2 ;
Solve1 ( S, T ) ;
Solve2 ( S, T ) ;
Solve3 ( S, T ) ;
return 0 ;
}