您的位置:首页 > 理论基础 > 计算机网络

LOJ6010 「网络流 24 题 - 11」数字梯形 坠大费用坠大流 坠大权不相交路径

2017-12-07 20:35 417 查看

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

题意:

  给定一个由 n行数字组成的数字梯形如下图所示。梯形的第一行有m个数字。从梯形的顶部的m个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。

  分别遵守以下规则:

从梯形的顶至底的m条路径互不相交;

从梯形的顶至底的m条路径仅在数字结点处相交;

从梯形的顶至底的m条路径允许在数字结点相交或边相交。

  要求输出三个任务的的最大值。

题解:

  坠大费用坠大流(这名字好迷)

  第一个任务 TASK1:

  老生常谈的每个点只经过一次⟶拆点建立分层图。即每个点a拆为a1,a2。

  建图:

建立超级源点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:

  每个点不要求只经过一次了,但是每条边只能经过一次。

  我们考虑如何在上面的方法改进。

  方法一:不拆点,限制边的流量为1。

  方法二:拆点,把a1→a2的流量设为+∞,a2→b1的连边流量为1,把最后一层的元素到汇点T的流量设为+∞。

  啦啦啦啦

  第三个任务 TASK3:

  乱跑就好了

  我们考虑如何在上面的方法改进。

  方法一:不拆点,限制边的流量为+∞。

  方法二:拆点,把a1→a2的流量设为+∞,a2→b1的连边流量为+∞,把最后一层的元素到汇点T的流量设为+∞。

  然后我调了一阵子发现时建图边界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
, dis
, pre
, 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
[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
[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
[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 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: