传送门:【HDU】4859
题目分析:
最小割的思想真是博大精深!
本题的模型是最小割。
我们需要最大化海岸线的长度,如果相邻两点属性不同才会存在海岸线(海和陆地),所以我们可以将题目转化成最小化不是海岸线的条数。
首先在地图的外围围上一圈‘D’,然后将整个图黑白染色(点的奇偶性),之后我们对所有相邻的点建边<u,v>,<v,u>,表示u能到v,v能到u。然后,我们对所有为'.'且在白色格子的或者为'D'且在黑色格子的点和源点(汇点)建边,对所有为'D'且在白色格子的或者为'.'且在黑色格子的点和汇点(源点)建边。
这样从源点到汇点则只能通过'.'->'.'或者'D'->'D'这样的边从源点流向汇点。
这样我们跑一遍最大流就知道了相邻点属性相同的边的最少条数(最小割)。
那么图中的'E'我们要不要管呢?我们可以设想,如果有流量通过E从源点到汇点,那么相当于将'E'定属性(海或陆地),这里最大流的意义就是:将'E'定属性以后尽量最大化相邻点属性相同的边的条数。通过流决定'E'的属性。
等等?刚才我们好像说是最大化?其实并没有错,就是最大化,这是最大流的属性,但是最大流与最小割是一个对偶问题,我们得到的最大流其实就是最小割,也就是说我们最大化后的结果其实就是要求的最少的不是海岸线的条数。是不是很神奇?
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define REV( i , n ) for ( int i = n - 1 ; i >= 0 ; -- i ) #define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define REPF( i , a , b ) for ( int i = a ; i < b ; ++ i ) #define REPV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i ) #define CLR( a , x ) memset ( a , x , sizeof a ) #define CPY( a , x ) memcpy ( a , x , sizeof a ) const int MAXG = 50 ; const int MAXN = 2505 ; const int MAXE = 1000000 ; const int MAXQ = 1000000 ; const int INF = 0x3f3f3f3f ; struct Edge { int v , c , n ; Edge () {} Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {} } ; struct NetWork { Edge E[MAXE] ; int H[MAXN] , cntE ; int d[MAXN] , cur[MAXN] , pre[MAXN] , num[MAXN] ; int Q[MAXQ] , head , tail ; int s , t , nv ; int flow ; int n , m ; char G[MAXG][MAXG] ; void init () { cntE = 0 ; CLR ( H , -1 ) ; } void addedge ( int u , int v , int c , int rc = 0 ) { E[cntE] = Edge ( v , c , H[u] ) ; H[u] = cntE ++ ; E[cntE] = Edge ( u , rc , H[v] ) ; H[v] = cntE ++ ; } void rev_bfs () { CLR ( num , 0 ) ; CLR ( d , -1 ) ; head = tail = 0 ; Q[tail ++] = t ; d[t] = 0 ; num[d[t]] = 1 ; while ( head != tail ) { int u = Q[head ++] ; for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( ~d[v] ) continue ; d[v] = d[u] + 1 ; num[d[v]] ++ ; Q[tail ++] = v ; } } } int ISAP () { CPY ( cur , H ) ; rev_bfs () ; flow = 0 ; int u = pre[s] = s , i ; while ( d[s] < nv ) { if ( u == t ) { int f = INF , pos ; for ( i = s ; i != t ; i = E[cur[i]].v ) if ( f > E[cur[i]].c ) { f = E[cur[i]].c ; pos = i ; } for ( i = s ; i != t ; i = E[cur[i]].v ) { E[cur[i]].c -= f ; E[cur[i] ^ 1].c += f ; } flow += f ; u = pos ; } for ( i = cur[u] ; ~i ; i = E[i].n ) if ( E[i].c && d[u] == d[E[i].v] + 1 ) break ; if ( ~i ) { cur[u] = i ; pre[E[i].v] = u ; u = E[i].v ; } else { if ( 0 == ( -- num[d[u]] ) ) break ; int mmin = nv ; for ( i = H[u] ; ~i ; i = E[i].n ) if ( E[i].c && mmin > d[E[i].v] ) { cur[u] = i ; mmin = d[E[i].v] ; } d[u] = mmin + 1 ; num[d[u]] ++ ; u = pre[u] ; } } return flow ; } void input () { CLR ( G , 'D' ) ; scanf ( "%d%d" , &n , &m ) ; FOR ( i , 1 , n ) scanf ( "%s" , G[i] + 1 ) , G[i][m + 1] = 'D' ; n += 2 , m += 2 ; s = n * m ; t = s + 1 ; nv = t + 1 ; REP ( i , n ) REP ( j , m ) { if ( ( i + j ) % 2 && G[i][j] == '.' || ( i + j ) % 2 == 0 && G[i][j] == 'D' ) addedge ( s , i * m + j , INF ) ; if ( ( i + j ) % 2 && G[i][j] == 'D' || ( i + j ) % 2 == 0 && G[i][j] == '.' ) addedge ( i * m + j , t , INF ) ; if ( i < n - 1 ) addedge ( i * m + j , ( i + 1 ) * m + j , 1 , 1 ) ; if ( j < m - 1 ) addedge ( i * m + j , i * m + ( j + 1 ) , 1 , 1 ) ; } } void solve () { init () ; input () ; printf ( "%d\n" , ( n - 1 ) * m + n * ( m - 1 ) - ISAP () ) ; } } z ; int main () { int T , cas = 0 ; scanf ( "%d" , &T ) ; while ( T -- ) { printf ( "Case %d: " , ++ cas ) ; z.solve () ; } return 0 ; }