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 ; }
相关文章推荐
- LOJ6014「网络流 24 题 - 15」最长 k 可重区间集 坠大权不相交路径 坠大费用坠大流
- 【网络流24题】No.16 数字梯形问题 (不相交路径 最大费用流)
- [网络流24题] 16 数字梯形(最大权不相交路径 ,最小费用最大流)
- LOJ6122 「网络流 24 题 - 18」 航空路线问题 最长不相交路径 坠大费用坠大流
- 线性规划与网络流24题之数字梯形问题 最大权不相交路径
- loj6010「网络流 24 题」数字梯形(费用流)
- 【网络流24题】数字梯形(费用流)
- 【网络流24题】数字梯形(二分图+最大费用流)
- Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)
- 【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)
- loj6010「网络流 24 题」数字梯形
- 【网络流24题-16】数字梯形问题 费用流
- 【网络流24题】No.11(航空路线问题 最长不相交路径 最大费用流)
- 【网络流24题】【cogs738】【codevs1913】数字梯形
- 线性规划与网络流24题之最长k可重区间集问题 最大权不相交路径(最大费用最大流)
- 【网络流24题】 No.6 最长不减子序列问题 (最大流)[模型:最多不相交路径]
- 【网络流24题-16】数字梯形问题
- [网络流24题] 数字梯形
- cogs 738. [网络流24题] 数字梯形
- LOJ6008 「网络流 24 题 - 9」餐巾计划 坠小费用坠大流