题目链接:CodeVS2297
题目大意
把一个(
X×Y×Z
, 其中
X,Y,Z≤40
) 的切糕切成上下两半,且对于每一对(x,y),都有
1≤f(x,y)≤Z
。
满足条件:
1.
∑x,yV(x,y,f(x,y))
最小 ;
2. 对于每一对(
x1,y1
)和(
x2,y2
),(
|f1−f2|≤D
) 。
分析
啥?分成上下两份?最小割啊!!!
1. 对每一个点(
x,y,z
),变成一个容量为
v(x,y,z)
的边(即向上连一条边,因此要在顶层的上面再加一层)。
2. 对每个点,向(
x,y,z−D
)的周围四个点连容量为INF 的边( 保证不能跨D格的高度割)。
3. 为什么只能向(
z−D
)连而不能向(
z+D
)连 ? 因为向上连边会使最大流增大!
4. 超级源S向所有第一层的点连边(INF),超级汇T向所有的最最上层连边(INF)。
5. 直接把所有的点放在一起跑最大流–最小割。
PS.换了一个更快的代码
上代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std ;
const int N = 40 + 5 ;
const int M = 32e4 + 5 ;
const int INF = 0x3f3f3f3f ;
const int DIR[ 4 ][ 2 ] = { { -1 , 0 } , { 1 , 0 } , { 0 , 1 } , { 0 , -1 } } ;
int lx, ly, lz, D ;
// ID把三维点映射到一维
#define ID( x , y , z ) ( (z) * lx * ly + (y) * lx + (x) )
struct node {
int x, y, z, id ;
inline bool judge() {
return x >= 1 && x <= lx && y >= 1 && y <= ly && z >= 1 && z <= lz ;
}
inline node move( int a , int b , int c ) {
node temp = (node){ x + a , y + b , z + c , 0 } ;
temp.id = ID( temp.x , temp.y , temp.z ) ;
return temp ;
}
} S, T ;
int head[ N * N * N ], len ;
struct node_lib {
int to, val, next ;
inline void add( int a , int b , int c ) {
to = b, val = c ;
next = head[ a ], head[ a ] = len ++ ;
}
} lib[ M << 1 ] ;
inline void init() {
memset( head , 0xff , sizeof( head ) ) ;
scanf( "%d %d %d %d", &lx, &ly, &lz, &D ) ;
S = (node){ 0 , 0 , 0 , ID( 0 , 0 , 0 ) } ;
T = (node){ 0 , 0 , lz + 2 , ID( 0 , 0 , lz + 2 ) } ;
for ( int z = 1 ; z <= lz ; z ++ )
for ( int x = 1 ; x <= lx ; x ++ )
for ( int y = 1 ; y <= ly ; y ++ ) {
int a ; scanf( "%d", &a ) ; // 建图
node temp = (node){ x , y , z , ID( x , y , z ) } ;
node nown = temp.move( 0 , 0 , 1 ) ;
lib[ len ].add( temp.id , nown.id , a ) ;
lib[ len ].add( nown.id , temp.id , 0 ) ;
if ( z - D > 0 )
for ( int i = 0 ; i < 4 ; i ++ ) {
nown = temp.move( DIR[ i ][ 0 ] , DIR[ i ][ 1 ] , -D ) ;
if ( !nown.judge() ) continue ;
lib[ len ].add( temp.id , nown.id , INF ) ;
lib[ len ].add( nown.id , temp.id , 0 ) ;
}
}
for ( int x = 1 ; x <= lx ; x ++ )
for ( int y = 1 ; y <= ly ; y ++ ) {
node tmp1 = (node){ x , y , 1 , ID( x , y , 1 ) } ;
node tmp2 = (node){ x , y , lz + 1 , ID( x , y , lz + 1 ) } ;
lib[ len ].add( S.id , tmp1.id , INF ) ;
lib[ len ].add( tmp1.id , S.id , 0 ) ;
lib[ len ].add( tmp2.id , T.id , INF ) ;
lib[ len ].add( T.id , tmp2.id , 0 ) ;
}
}
// 以下是模板
queue < int > Q ;
int curn[ N * N * N ] ;
int step[ N * N * N ] ;
inline bool bfs() {
memset( step , 0 , sizeof( step ) ) ;
step[ S.id ] = 1 ; Q.push( S.id ) ;
while ( !Q.empty() ) {
int temp = Q.front() ; Q.pop() ;
for ( int p = head[ temp ] ; ~ p ; p = lib[ p ].next ) {
int nown = lib[ p ].to ;
if ( !step[ nown ] && lib[ p ].val ) {
step[ nown ] = step[ temp ] + 1 ; Q.push( nown ) ;
}
}
}
if ( step[ T.id ] ) return true ;
else return false ;
}
int dfs( int a , int b ) {
if ( a == T.id ) return b ;
for ( int &p = curn[ a ] ; ( ~ p ) && b ; p = lib[ p ].next ) {
int nown = lib[ p ].to ;
if ( step[ nown ] == step[ a ] + 1 && lib[ p ].val ) {
int temp = dfs( nown , min( b , lib[ p ].val ) ) ;
if ( temp ) {
lib[ p ].val -= temp ; lib[ p ^ 1 ].val += temp ;
return temp ;
}
}
}
return 0 ;
}
inline int figure() {
int ans = 0 ;
while ( bfs() ) {
for ( int x = 1 ; x <= lx ; x ++ )
for ( int y = 1 ; y <= ly ; y ++ )
for ( int z = 1 ; z <= lz + 1 ; z ++ ) {
int id = ID( x , y , z ) ;
curn[ id ] = head[ id ] ;
}
curn[ S.id ] = head[ S.id ], curn[ T.id ] = head[ T.id ] ;
while ( int temp = dfs( S.id , INF ) )
ans += temp ;
}
return ans ;
}
int main() {
init() ;
printf( "%d\n", figure() ) ;
return 0 ;
}
以上